﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>IT博客-嵌入式-文章分类-嵌入式技术资料</title><link>http://www.cnitblog.com/zouzheng/category/2077.html</link><description>要像阿甘一直向前奔跑！</description><language>zh-cn</language><lastBuildDate>Mon, 26 Sep 2011 09:16:12 GMT</lastBuildDate><pubDate>Mon, 26 Sep 2011 09:16:12 GMT</pubDate><ttl>60</ttl><item><title>Building arm-linux toolchain for ARM/XScale-iWMMXt(new ABI)</title><link>http://www.cnitblog.com/zouzheng/articles/40304.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Fri, 29 Feb 2008 09:41:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/40304.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/40304.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/40304.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/40304.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/40304.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;[本文PDF文档点此下载]0 Preface两个月前，笔者在ChinaUnix BLOG上发表了一篇日志Building arm-linux toolchain for ARM/XSCALE，详细介绍了手工建立ARM-linux的交叉编译工具链的全过程，但这种方法越来越体现出局限性和落后性，主要表现在：(1)使用的软件包版本较老；(2)使用linux-threads而非新的...&nbsp;&nbsp;<a href='http://www.cnitblog.com/zouzheng/articles/40304.html'>阅读全文</a><img src ="http://www.cnitblog.com/zouzheng/aggbug/40304.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2008-02-29 17:41 <a href="http://www.cnitblog.com/zouzheng/articles/40304.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>HOWTO build arm-linux toolchain for ARM/XSCALE</title><link>http://www.cnitblog.com/zouzheng/articles/40216.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Wed, 27 Feb 2008 08:48:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/40216.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/40216.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/40216.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/40216.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/40216.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: These instructions document how to build an arm-linux tool chainthat contains both little-endian and big-endian target libraries.1. Packages used:&nbsp;&nbsp;&nbsp; binutils-2.14.tar.gz&nbsp;&nbsp;&nb...&nbsp;&nbsp;<a href='http://www.cnitblog.com/zouzheng/articles/40216.html'>阅读全文</a><img src ="http://www.cnitblog.com/zouzheng/aggbug/40216.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2008-02-27 16:48 <a href="http://www.cnitblog.com/zouzheng/articles/40216.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>arm linux 启动流程</title><link>http://www.cnitblog.com/zouzheng/articles/40213.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Wed, 27 Feb 2008 08:28:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/40213.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/40213.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/40213.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/40213.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/40213.html</trackback:ping><description><![CDATA[<p>arm linux 启动流程之 ppcboot&nbsp;&nbsp;&nbsp; <br><a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#65;&#117;&#116;&#104;&#111;&#114;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#68;&#97;&#110;&#115;&#101;&#110;&#45;&#45;&#45;&#45;&#45;&#120;&#122;&#100;&#50;&#55;&#51;&#52;&#64;&#49;&#54;&#51;&#46;&#99;&#111;&#109;"><u><font color=#0000ff>Author-------Dansen-----xzd2734@163.com</font></u></a></p>
<p>不是每一行代码都必须读懂，我只是大概地过一下流程<br>毕竟这些都是比较成熟的代码，没必要去改的<br>是针对我自己的板子的，硬件配置如下<br>cpu是s3c2410<br>board type 是 smdk2410<br>16M Nor Flash 地址是 0x0---0xFFFFFF<br>64M SDRAM&nbsp;&nbsp;&nbsp;&nbsp; 地址是 0x30000000---0x33FFFFFF<br>软件是华恒版的<br>ppcboot 2.0 和 linux 2.4.18<br>仔细分析了一下启动的流程，能更好地理解硬件和软件的配合<br>方便移植。<br>我们在flash的开始处烧写了ppcboot.bin,这是可执行的二进制文件<br>注意和ELF可执行性文件是有区别的。<br>cpu上电后可以从直接从flash地址0处取指令来执行<br>开始的代码在ppcboot-2.0.0\cpu\arm920t\start.s中<br>这里需要提一下编译链接时用到的一个很重要的链接文件<br>ppcboot-2.0.0\board\smdk2410\ppcboot.lds<br>这个文件给出了代码中各标号的基地址，和各个段的链接顺序<br>ENTRY(_start)<br>SECTIONS<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; . = 0x00000000;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; . = ALIGN(4);<br>&nbsp;.text&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; :<br>&nbsp;{<br>&nbsp;&nbsp; cpu/arm920t/start.o (.text)<br>&nbsp;&nbsp; *(.text)<br>&nbsp;}</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; . = ALIGN(4);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .rodata : { *(.rodata) }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; . = ALIGN(4);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .data : { *(.data) }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; . = ALIGN(4);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .got : { *(.got) }</p>
<p>&nbsp;armboot_end_data = .;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; . = ALIGN(4);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .bss : { *(.bss) }</p>
<p>&nbsp;armboot_end = .;<br>}<br>可以看到程序的入口是_start标号指示的，而cpu/arm920t/start.o<br>则被安排在程序最开始的地方，这个标号就是在start.s中<br>但是还有一点是需要特别注意的，开始我也是因为这个地方而没有很好地理解程序<br>虽然lds中有 . = 0x00000000 这一句，指示链接基地址，不过其实这句是不起作用的，<br>真正的链接基地址在ppcboot-2.0.0\config.mk中指定的<br>LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE)<br>其中-Ttext $(TEXT_BASE)就是指定链接地址为TEXT_BASE的值<br>因而是可变的，TEXT_BASE在ppcboot-2.0.0\board\smdk2410\config.mk中定义<br>TEXT_BASE = 0x33F00000<br>ppcboot-2.0.0\config.mk是包括到Makefile中的,<br>在Makefile中有$(LD) $(LDFLAGS) $(OBJS) $(LIBS) $(LIBS) -Map ppcboot.map -o ppcboot<br>所以说起来真正的链接地址是0x33F00000,其实这样在把ppcboot拷到Ram中就可以实现无缝跳转了<br>.globl _start<br>_start: b&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; reset<br>跳到renset<br>reset: ldr&nbsp;&nbsp;&nbsp;&nbsp; r0, =pWTCON<br>&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp; r1, #0x0<br>&nbsp;str&nbsp;&nbsp;&nbsp;&nbsp; r1, [r0]<br>..........................<br>&nbsp;bl cpu_init_crit&nbsp; //bl跳转会回来<br>relocate: //下面开始要把ppcboot拷到Ram中<br>&nbsp;adr r0, _start&nbsp; /* r0 &lt;- current position of code */<br>&nbsp;ldr r2, _armboot_start<br>&nbsp;ldr r3, _armboot_end<br>&nbsp;sub r2, r3, r2&nbsp; /* r2 &lt;- size of armboot */<br>&nbsp;ldr r1, _TEXT_BASE&nbsp; /* r1 &lt;- destination address */<br>&nbsp;add r2, r0, r2&nbsp; /* r2 &lt;- source end address */<br>以上代码需要注意的一点是 adr 和 ldr 的区别<br>adr取得是当前pc相关的偏移地址,在这里程序还是在flash中运行<br>所以取得地址是以0x0为基址的<br>而ldr取的是_armboot_start所指的值<br>.globl _armboot_start<br>_armboot_start:<br>&nbsp;.word _start<br>看到它的值也是_start的地址,不过我们这里取的是绝对地址,是在链接是确定的以<br>TEXT_BASE为基址的.由于_start的偏移是0,所以r0是0,r2就是TEXT_BASE<br>copy_loop:<br>&nbsp;ldmia r0!, {r3-r10}<br>&nbsp;stmia r1!, {r3-r10}<br>&nbsp;cmp r0, r2<br>&nbsp;ble copy_loop<br>循环copy<br>&nbsp;ldr r0, _armboot_end&nbsp; /* set up the stack */<br>&nbsp;add r0, r0, #CONFIG_STACKSIZE<br>&nbsp;sub sp, r0, #12&nbsp; /* leave 3 words for abort-stack */</p>
<p>&nbsp;ldr pc, _start_armboot <br>_start_armboot: .word start_armboot</p>
<p>//通过这一句跳转到ppcboot-2.0.0\lib_arm\board.c中的start_armboot函数去执行了<br>start_armboot的绝对地址也是以TEXT_BASE为基址的,所以可以顺利的实现无缝跳转了.<br>接着下来就是一系列初始化的工作了<br>首先定义了一个全局的数据结构 gd_t gd_data;<br>DECLARE_GLOBAL_DATA_PTR 这个宏定义的是一个全局的gd_t类型的指针gd<br>gd = &amp;gd_data;<br>这样以后就可以用gd来访问gd_data这个数据结构了<br>&nbsp;for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {<br>&nbsp; if ((*init_fnc_ptr)() != 0) {<br>&nbsp;&nbsp; hang ();<br>&nbsp; }<br>&nbsp;}<br>init_fnc_ptr中是一系列初始化函数的指针<br>init_fnc_t *init_sequence[] = {<br>&nbsp;cpu_init,&nbsp; /* basic cpu dependent setup */<br>&nbsp;board_init,&nbsp; /* basic board dependent setup */<br>&nbsp;interrupt_init,&nbsp; /* set up exceptions */<br>&nbsp;env_init,&nbsp; /* initialize environment */<br>&nbsp;init_baudrate,&nbsp; /* initialze baudrate settings */<br>&nbsp;serial_init,&nbsp; /* serial communications setup */<br>&nbsp;display_banner,<br>&nbsp;dram_init,&nbsp; /* configure available RAM banks */<br>&nbsp;display_dram_config,</p>
<p>&nbsp;NULL,<br>};<br>基本上serial_init后我们就可以用printf函数来打印信息了.<br>&nbsp;for (;;) {<br>&nbsp; main_loop ();<br>&nbsp;}<br>进入了主循环,在M:\ppcboot-2.0.0\common\main.c中<br>&nbsp;{<br>&nbsp; char c = 'y';<br>&nbsp; unsigned long timedata;<br>&nbsp; printf("start linux now(y/n):");<br>&nbsp; timedata = 0;<br>&nbsp; for (;;) {<br>&nbsp;&nbsp; while (!tstc()) { /* while no incoming data */<br>&nbsp;&nbsp;&nbsp; if (timedata++ &gt; 3000 * 100 *3)<br>&nbsp;&nbsp;&nbsp;&nbsp; goto bootm; /* timed out */<br>&nbsp;&nbsp; }<br>&nbsp; c = getc();<br>&nbsp; }<br>tstc()是测试串口是否有数据输入,显然没有的话就会等待time out跳出<br>bootm:<br>&nbsp; if(c == 'y'||c == 'Y'){<br>&nbsp;&nbsp; strcpy(lastcommand , "bootm 30008000 30800000\r");<br>&nbsp;&nbsp; flag = 0;<br>&nbsp;&nbsp; rc = run_command (lastcommand, flag);<br>&nbsp;&nbsp; if (rc &lt;= 0) {<br>&nbsp;&nbsp;&nbsp; /* invalid command or not repeatable, forget it */<br>&nbsp;&nbsp;&nbsp; lastcommand[0] = 0;<br>&nbsp;&nbsp; }<br>&nbsp; }<br>&nbsp; else{<br>&nbsp;&nbsp; printf("\n\n");<br>&nbsp; }<br>&nbsp;} <br>这样如果串口没有输入或者输入时y Y 的话,就会去执行bootm 30008000 30800000\r这条命令<br>否则就会到ppcboot的命令行等待输入.<br>执行bootm 30008000 30800000\r这条命令会调用ppcboot-2.0.0\common\cmd_bootm.c中的<br>do_bootm函数,具体的命令怎样被分解,选择调用函数的机制我就不多说了,追着run_command去就是了<br>在do_bootm中调用了do_bootm_linux函数,这个函数在ppcboot-2.0.0\lib_arm\armlinux.c中<br>&nbsp;&nbsp;&nbsp; ret = memcpy((void *)0x30008000, (void *)0x40000, 0x100000);<br>&nbsp;&nbsp;&nbsp; if (ret != (void *)0x30008000)<br>&nbsp;&nbsp;&nbsp;&nbsp; printf("copy kernel failed\n");<br>&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp; printf("copy kernel done\n");&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; ret = memcpy((void *)0x30800000, (void *)0x140000, 0x440000);<br>&nbsp;&nbsp;&nbsp; if (ret != (void *)0x30800000)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("haha failed\n");<br>&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("copy ramdisk done\n");</p>
<p>首先把kernel和ramdisk都拷到Ram相应的地方去.<br>&nbsp;&nbsp;&nbsp; setup_linux_param(0x30000000 + LINUX_PARAM_OFFSET); //也在armlinux.c中<br>&nbsp;&nbsp;&nbsp; #define LINUX_PARAM_OFFSET 0x100<br>建立要传给内核的参数,参数的地址都是固定的,所以内核也知道去这里取参数<br>参数格式比较复杂,我这里好像传得参数不多<br>void setup_linux_param(ulong param_base)<br>{<br>&nbsp;struct param_struct *params = (struct param_struct *)param_base;<br>...............<br>}<br>只是通过一个param_struct的结构体来传参数的,不过现在一般都用另一种tag标记的传参方法<br>一个主要的参数时char linux_cmd[] = "initrd=0x30800000,0x440000&nbsp; root=/dev/ram init=/linuxrc console=ttyS0";<br>&nbsp;if (linux_cmd == NULL) {<br>&nbsp; printf("Wrong magic: could not found linux command line\n");<br>&nbsp;} else {<br>&nbsp; memcpy(params-&gt;commandline, linux_cmd, strlen(linux_cmd) + 1);<br>&nbsp; printf("linux command line is: \"%s\"\n", linux_cmd);<br>&nbsp;}<br>是比较重要的.移植的时候常常需要修改<br>接着call_linux(0, 0xc1, 0x30008000); 看出来是准备调到linux去了<br>0xc1是machine type,这三个参数分别给了r0,r1,r2,这些都是调用内核的约定<br>void&nbsp; call_linux(long a0, long a1, long a2)<br>{<br>__asm__(<br>&nbsp;"mov r0, %0\n"<br>&nbsp;"mov r1, %1\n"<br>&nbsp;"mov r2, %2\n"<br>&nbsp;"mov ip, #0\n"<br>&nbsp;"mcr p15, 0, ip, c13, c0, 0\n" /* zero PID */<br>&nbsp;"mcr p15, 0, ip, c7, c7, 0\n" /* invalidate I,D caches */<br>&nbsp;"mcr p15, 0, ip, c7, c10, 4\n" /* drain write buffer */<br>&nbsp;"mcr p15, 0, ip, c8, c7, 0\n" /* invalidate I,D TLBs */<br>&nbsp;"mrc p15, 0, ip, c1, c0, 0\n" /* get control register */<br>&nbsp;"bic ip, ip, #0x0001\n"&nbsp; /* disable MMU */<br>&nbsp;"mcr p15, 0, ip, c1, c0, 0\n" /* write control register */<br>&nbsp;"mov pc, r2\n"<br>&nbsp;"nop\n"<br>&nbsp;"nop\n"<br>&nbsp;: /* no outpus */<br>&nbsp;: "r" (a0), "r" (a1), "r" (a2)<br>&nbsp;);<br>}<br>mov pc, r2 就是这句吧,调到了30008000去执行内核了</p>
<p>接下来就到内核了吧</p>
<br>
<p>arm linux 启动流程之 解压内核&nbsp;&nbsp;&nbsp;</p>
<p><a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#65;&#117;&#116;&#104;&#111;&#114;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#68;&#97;&#110;&#115;&#101;&#110;&#45;&#45;&#45;&#45;&#45;&#120;&#122;&#100;&#50;&#55;&#51;&#52;&#64;&#49;&#54;&#51;&#46;&#99;&#111;&#109;">Author-------Dansen-----xzd2734@163.com</a></p>
<p>从后往前看下编译生成zImage的过程，我们可以找到程序的入口还是那个很重要<br>链接文件，找到它,生成zImage所在的目录是kernel\arch\arm\boot\compressed\<br>Make过程为....ld -p -X -T vmlinux.lds head.o misc.o head-s3c2410.o piggy.o<br>libgcc.o -o vmlinux<br>然后是用二进制工具objcopy把vmlinux制作成可执行的二进制映像文件zImage<br>这样在我们就去kernel\arch\arm\boot\compressed\目录下去找到vmlinux.lds文件<br>如果没有编译就不会有这个文件，因为它也是在编译过程生成的，由同一目录下的<br>vmlinux.lds.in生成，打开这个文件<br>ENTRY(_start)<br>SECTIONS<br>{<br>&nbsp; . = LOAD_ADDR;<br>&nbsp; _load_addr = .;</p>
<p>&nbsp; . = TEXT_START;<br>&nbsp; _text = .;</p>
<p>&nbsp; .text : {<br>&nbsp;&nbsp;&nbsp; _start = .;<br>&nbsp;&nbsp;&nbsp; *(.start)<br>&nbsp;&nbsp;&nbsp; *(.text)<br>........<br>入口是_start,而且入口就直接定义在这个文件中了<br>入口直接接着.start段,所以程序开始是从.start段开始执行的<br>如果看看vmlinux.lds的生成过程就应该能找到LOAD_ADDR和TEXT_START的值<br>实际上这两个值是由其他两个变量赋给的 ZRELADDR 和 ZTEXTADDR<br>在kernel\arch\arm\boot\Makefile中我们可以找到这两个变量的值<br>ifeq ($(CONFIG_ARCH_S3C2410),y)<br>ZTEXTADDR&nbsp; = 0x30008000<br>ZRELADDR&nbsp; = 0x30008000<br>endif<br>所以<br>LOAD_ADDR = 0x30008000<br>TEXT_START = 0x30008000<br>看一下vmlinux.lds吧<br>ENTRY(_start)<br>SECTIONS<br>{<br>&nbsp; . = 0x30008000;<br>&nbsp; _load_addr = .;</p>
<p>&nbsp; . = 0;<br>&nbsp; _text = .;<br>显然LOAD_ADDR被赋值了0x30008000<br>看一下TEXT_START怎么成0了，我想这应该是一个偏移吧，偏移是0<br>所以它还是0x30008000<br>接着下来就从head.s来开始看代码吧<br>&nbsp; .section ".start", #alloc, #execinstr<br>/*<br>&nbsp;* sort out different calling conventions<br>&nbsp;*/<br>&nbsp; .align<br>start:<br>&nbsp; .type start,#function<br>&nbsp; .rept 8<br>&nbsp; mov r0, r0<br>&nbsp; .endr</p>
<p>&nbsp; b 1f<br>&nbsp; .word 0x016f2818&nbsp; @ Magic numbers to help the loader<br>&nbsp; .word start&nbsp;&nbsp; @ absolute load/run zImage address<br>&nbsp; .word _edata&nbsp;&nbsp; @ zImage end address<br>1:&nbsp; mov r7, r1&nbsp;&nbsp; @ save architecture ID<br>这里一定就是程序的入口了，一般汇编程序的含义就看看英文注释就是了<br>有一个要注意的地方，不是一个汇编文件就是属于一个段的，不是说先执行完了<br>head.s再去执行head-s3c2410.s,还是要注意链接的段,显然head.s<br>不一会就开始了另一个段.text<br>&nbsp; .text<br>&nbsp; adr r0, LC0<br>&nbsp; ldmia r0, {r1, r2, r3, r4, r5, r6, ip, sp}<br>&nbsp; subs r0, r0, r1&nbsp; @ calculate the delta offset<br>而我们的head-s3c2410.s呢<br>&nbsp;.section ".start", #alloc, #execinstr<br>__S3C2410_start:<br>&nbsp;bic r2, pc, #0x1f<br>&nbsp;add r3, r2, #0x4000&nbsp; @ 16 kb is quite enough...<br>还是属于.start段的，所以顺序执行下来时先执行head-s3c2410.s，然后再去执行<br>.text段。head-s3c2410.s主要是cpu的一些初始化工作。接着下来我们会需要把内核<br>接压缩，先说说为什么吧。还是注意到上面生成zImage的文件中有一个piggy.o，往上<br>追寻可以看到是piggy.o由那个真正的内核vmlinux生成的，这个vmlinux才是启动后一直在<br>运行的内核，原本很大，压缩以后可以方便地放在flash中，当然其实不压缩跳到它的<br>入口也就可以运行了。解压的内核是准备从LOAD_ADDR = 0x30008000开始的4M空间，会覆盖<br>我们的当前运行的代码，那样就先把内核解压到我们这个zImage+分配堆栈0x10000的最后<br>&nbsp; cmp r4, r2&nbsp; //r4 是LOAD_ADDR=0x30008000<br>&nbsp; bhs wont_overwrite //r2 是当前代码的最底部&nbsp;&nbsp;&nbsp; 这里当然不会跳转<br>&nbsp; add r0, r4, #4096*1024 @ 4MB largest kernel size<br>&nbsp; cmp r0, r5&nbsp; //r5 也是0x30008000 <br>&nbsp; bls wont_overwrite //不会跳转</p>
<p>&nbsp; mov r5, r2&nbsp; //r2是(user_stack+4096)在zImage的最后+0x10000<br>&nbsp; mov r0, r5&nbsp; <br>&nbsp; mov r3, r7&nbsp; //machine type<br>&nbsp; bl decompress_kernel <br>有了r5,r0,r7作为参数，就可以调用misc.c中的decompress_kernel函数进行解压缩了<br>这个函数调用的gunzip函数时gcc的库函数，所以在源码中找不到的<br>解压在r5开始的地方，函数返回的是r0解压得到的长度。这时候我们需要对代码经行调整<br>&nbsp; add r1, r5, r0&nbsp; @ end of decompressed kernel<br>&nbsp; adr r2, reloc_start<br>&nbsp; ldr r3, LC1&nbsp;&nbsp; //LC1: .word reloc_end - reloc_start<br>&nbsp; add r3, r2, r3<br>1:&nbsp; ldmia r2!, {r8 - r13}&nbsp; @ copy relocation code<br>&nbsp; stmia r1!, {r8 - r13}<br>&nbsp; ldmia r2!, {r8 - r13}<br>&nbsp; stmia r1!, {r8 - r13}<br>&nbsp; cmp r2, r3&nbsp; //这里就把从reloc_start到reloc_end这段我们需要的代码放到了<br>&nbsp; blo 1b&nbsp; //解压内核的最后，而在下面我们会将zImage都覆盖掉<br>&nbsp; bl cache_clean_flush<br>&nbsp; add pc, r5, r0 //调到调整后的reloc_start，在decompressed kernel后<br>reloc_start: add r8, r5, r0 //r5解压内核开始的地方 r0解压内核的长度<br>&nbsp; debug_reloc_start<br>&nbsp; mov r1, r4&nbsp; //r4=0x30008000<br>1:<br>&nbsp; .rept 4<br>&nbsp; ldmia r5!, {r0, r2, r3, r9 - r13} @ relocate kernel<br>&nbsp; stmia r1!, {r0, r2, r3, r9 - r13}<br>&nbsp; .endr</p>
<p>&nbsp; cmp r5, r8<br>&nbsp; blo 1b&nbsp; //这样就又把解压的真正内核移到了0x30008000处<br>call_kernel: bl cache_clean_flush<br>&nbsp; bl cache_off<br>&nbsp; mov r0, #0<br>&nbsp; mov r1, r7&nbsp;&nbsp; @ restore architecture number<br>&nbsp; mov pc, r4&nbsp;&nbsp; @ call kernel<br>上面就是跳到0x30008000这里去执行真正的内核了吧</p>
<br><br>
<p>arm linux 启动流程之 进入内核&nbsp;&nbsp; </p>
<p><a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#65;&#117;&#116;&#104;&#111;&#114;&#45;&#45;&#45;&#45;&#45;&#45;&#45;&#68;&#97;&#110;&#115;&#101;&#110;&#45;&#45;&#45;&#45;&#45;&#120;&#122;&#100;&#50;&#55;&#51;&#52;&#64;&#49;&#54;&#51;&#46;&#99;&#111;&#109;">Author-------Dansen-----xzd2734@163.com</a> </p>
<p>还是从编译链接生成vmlinux的过程来看吧，由一大堆.o文件链接而成，第一个就是<br>kernel\arch\arm\kernel\head-armv.o ,而且我们还看到了<br>lds链接文件kernel\arch\arm\vmlinux.lds,先把它分析一下<br>ENTRY(stext) //入口点是stext 应该就在head-armv.s中了<br>SECTIONS<br>{<br>&nbsp;. = 0xC0008000;&nbsp; //基址，是内核开始的虚拟地址<br>&nbsp;.init : {&nbsp;&nbsp; /* Init code and data&nbsp; */<br>&nbsp; _stext = .;<br>&nbsp; __init_begin = .;<br>&nbsp;&nbsp; *(.text.init)<br>&nbsp; __proc_info_begin = .;<br>&nbsp;&nbsp; *(.proc.info)<br>&nbsp; __proc_info_end = .;<br>&nbsp; __arch_info_begin = .;<br>&nbsp;&nbsp; *(.arch.info)<br>&nbsp; __arch_info_end = .;<br>&nbsp; __tagtable_begin = .;<br>&nbsp;&nbsp; *(.taglist)<br>&nbsp; __tagtable_end = .;<br>&nbsp;&nbsp; *(.data.init)<br>&nbsp; . = ALIGN(16);<br>&nbsp; __setup_start = .;<br>&nbsp;&nbsp; *(.setup.init)<br>&nbsp; __setup_end = .;<br>&nbsp; __initcall_start = .;<br>&nbsp;&nbsp; *(.initcall.init)<br>&nbsp; __initcall_end = .;<br>&nbsp; . = ALIGN(4096);<br>&nbsp; __init_end = .;<br>&nbsp;}<br>关于虚拟地址和物理地址的：使用MMU后，系统就会使用虚拟地址，通过MMU来指向<br>实际物理地址而在这里我们的0xC0008000实际物理地址就是0x30008000，<br>具体关于MMU的介绍参考《ARM体系结构与编程》。<br>到head-armv.s找到程序的入口<br>&nbsp; .section ".text.init",#alloc,#execinstr<br>&nbsp; .type stext, #function<br>ENTRY(stext)<br>&nbsp; mov r12, r0<br>&nbsp; mov r0, #F_BIT | I_BIT | MODE_SVC @ make sure svc mode<br>&nbsp; msr cpsr_c, r0&nbsp;&nbsp; @ and all irqs disabled<br>&nbsp; bl __lookup_processor_type<br>&nbsp; teq r10, #0&nbsp;&nbsp;&nbsp; @ invalid processor?<br>&nbsp; moveq r0, #'p'&nbsp;&nbsp; @ yes, error 'p'<br>&nbsp; beq __error<br>&nbsp; bl __lookup_architecture_type<br>&nbsp; teq r7, #0&nbsp;&nbsp;&nbsp; @ invalid architecture?<br>&nbsp; moveq r0, #'a'&nbsp;&nbsp; @ yes, error 'a'<br>&nbsp; beq __error<br>&nbsp; bl __create_page_tables<br>&nbsp; adr lr, __ret&nbsp;&nbsp; @ return address<br>&nbsp; add pc, r10, #12&nbsp;&nbsp; @ initialise processor<br>来看看上一句跳到哪里去了<br>去追寻r10的值，是在__lookup_processor_type子函数中赋的<br>__lookup_processor_type:<br>&nbsp; adr r5, 2f&nbsp;&nbsp; //r5 标号2的地址 基址是0x30008000<br>&nbsp; ldmia r5, {r7, r9, r10} //r7=__proc_info_end&nbsp; r9=__proc_info_begin<br>&nbsp; sub r5, r5, r10&nbsp; //r10 标号2的链接地址&nbsp;&nbsp; 基址是0xc0008000<br>&nbsp; add r7, r7, r5&nbsp;&nbsp; @ to our address space<br>&nbsp; add r10, r9, r5&nbsp; //r10 变换为基址是0x30008000的__proc_info_begin<br>2:&nbsp; .long __proc_info_end<br>&nbsp; .long __proc_info_begin<br>&nbsp; .long 2b<br>这样r10中存放的是__proc_info_begin的地址,因为现在我们还没有打开MMU<br>所以还是需要把基址变换到0x30008000,接着我们就去找__proc_info_begin吧<br>注意到在上面的vmlinux.lds中有这个标号,下来链接的是.proc.info段,<br>在kernel\arch\arm\mm\proc-arm920.s的最后找到了这个段<br>&nbsp;.section ".proc.info", #alloc, #execinstr</p>
<p>&nbsp;.type __arm920_proc_info,#object<br>__arm920_proc_info:<br>&nbsp;.long 0x41009200<br>&nbsp;.long 0xff00fff0<br>&nbsp;.long 0x00000c1e&nbsp;&nbsp; @ mmuflags<br>&nbsp;b __arm920_setup<br>ok,这样我们就知道add pc, r10, #12跳到哪里去了,因为这个地址刚好放了条跳转语句<br>注意了b语句用的都是相对地址,所以不需要变换地址,反正是跳到__arm920_setup,而且<br>上一条语句是adr lr, __ret,设定了__arm920_setup的返回地址是__ret,所以执行完<br>__arm920_setup后回到head-armv.s的__ret标号继续执行.<br>__ret:&nbsp; ldr lr, __switch_data<br>&nbsp; mcr p15, 0, r0, c1, c0 //注意这里了,在这里打开了MMU<br>&nbsp; mov r0, r0<br>&nbsp; mov r0, r0<br>&nbsp; mov r0, r0<br>&nbsp; mov pc, lr //跳到__mmap_switched,这里已经用了虚拟地址了吧<br>&nbsp;// 这条指令ldr lr, __switch_data加载的__mmap_switched地址就是虚拟地址啊<br>__switch_data: .long __mmap_switched<br>从__mmap_switched一路执行下来,就要调到C语言代码中去了<br>&nbsp; b SYMBOL_NAME(start_kernel) //在kernel\init\main.c中<br>这个程序不是特别复杂，细心看看还是能大概看懂，我也不能去一一注释<br>这里有一个流程图</p>
<p><img alt="" src="http://p.blog.csdn.net/images/p_blog_csdn_net/dansen_xu/Drawing1.jpg"></p>
<p>到了C语言中就不是很难理解了<br>&nbsp;lock_kernel();<br>&nbsp;printk(linux_banner);<br>&nbsp;setup_arch(&amp;command_line);<br>&nbsp;printk("Kernel command line: %s\n", saved_command_line);<br>&nbsp;parse_options(command_line);<br>&nbsp;trap_init();<br>&nbsp;init_IRQ();<br>&nbsp;sched_init();<br>&nbsp;softirq_init();<br>&nbsp;time_init();<br>就是一大堆初始化工作,追着每个函数去看好了</p>
<p>start_kernel最后调用的一个函数<br>static void rest_init(void)<br>{<br>&nbsp;kernel_thread(init, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL);<br>&nbsp;unlock_kernel();<br>&nbsp;current-&gt;need_resched = 1;<br>&nbsp; cpu_idle();<br>}<br>用kernel_thread建立了一个init进程,执行的是main.c中的init函数<br>&nbsp;lock_kernel();<br>&nbsp;do_basic_setup();<br>在do_basic_setup中调用了do_initcalls函数<br>各种驱动都是在do_initcalls(void)中完成的<br>static void __init do_initcalls(void)<br>{<br>&nbsp;initcall_t *call;</p>
<p>&nbsp;call = &amp;__initcall_start;<br>&nbsp;do {<br>&nbsp; (*call)();<br>&nbsp; call++;<br>&nbsp;} while (call &lt; &amp;__initcall_end);</p>
<p>&nbsp;flush_scheduled_tasks();<br>}<br>__initcall_start也是在vmlinux.lds中赋值的,那就需要找到.initcall.ini这个段<br>在kernel\include\linux\init.h中可以找到<br>#define __init_call __attribute__ ((unused,__section__ (".initcall.init")))<br>typedef int (*initcall_t)(void);<br>#define __initcall(fn)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \<br>&nbsp;static initcall_t __initcall_##fn __init_call = fn<br>仔细研究下就发现这是把初始化函数的地址放到了.initcall.init段中<br>这样就可以不断调用驱动的初始化函数了<br>如果没有定义MODULE,那么#define module_init(x) __initcall(x);<br>所以如果要把驱动的编译进内核就很简单了吧<br>init的最后<br>&nbsp;if (execute_command)<br>&nbsp; execve(execute_command,argv_init,envp_init);<br>execute_command与ppcboot传的命令行参数是有关的哦,就是init=/linuxrc<br>这样就要去执行根目录下的linuxrc脚本,这个脚本会去执行busybox<br>而busybox又去执行/etc/init.d/rcS脚本,这个脚本又去执行/usr/etc/rc.local<br>完了</p>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/40213.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2008-02-27 16:28 <a href="http://www.cnitblog.com/zouzheng/articles/40213.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>u-boot启动过程分析——基于lpc2210的移植代码</title><link>http://www.cnitblog.com/zouzheng/articles/40174.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Tue, 26 Feb 2008 06:51:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/40174.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/40174.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/40174.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/40174.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/40174.html</trackback:ping><description><![CDATA[<p><em>作者：华清远见嵌入式培训中心（</em><em>转载请注明出处）</em>
<p>&nbsp;</p>
<div>u-boot是一种普遍用于嵌入式系统中的Bootloader。 </div>
<p>&nbsp;</p>
<div><strong><font color=#0938f7>Bootloader介绍</font></strong> </div>
<p>&nbsp;</p>
<div>Bootloader的定义：<u>Bootloader是在操作系统运行之前执行的一小段程序，通过这一小段程序，我们可以初始化硬件设备、建立内存空间的映射表，从而建立适当的系统软硬件环境，为最终调用操作系统内核做好准备</u>。意思就是说如果我们要想让一个操作系统在我们的板子上运转起来，我们就必须首先对我们的板子进行一些基本配置和初始化，然后才可以将操作系统引导进来运行。具体在Bootloader中完成了哪些操作我们会在后面分析到，这里我们先来回忆一下PC的体系结构：<u>PC机中的引导加载程序是由BIOS和位于硬盘MBR中的OS Boot Loader（比如LILO和GRUB等）一起组成的，BIOS在完成硬件检测和资源分配后，将硬盘MBR中的Boot Loader读到系统的RAM中，然后将控制权交给OS Boot Loader。Boot Loader的主要运行任务就是将内核映象从硬盘上读到RAM中，然后跳转到内核的入口点去运行，即开始启动操作系统</u>。在嵌入式系统中，通常并没有像BIOS那样的固件程序（注：有的嵌入式cpu也会内嵌一段短小的启动程序），因此整个系统的加载启动任务就完全由Boot Loader来完成。比如在一个基于ARM7TDMI core的嵌入式系统中，系统在上电或复位时通常都从地址0x00000000处开始执行，而在这个地址处安排的通常就是系统的Boot Loader程序。（<em>先想一下，通用PC和嵌入式系统为何会在此处存在如此的差异呢</em>？） </div>
<p>&nbsp;</p>
<div>Bootloader是基于特定硬件平台来实现的，因此几乎不可能为所有的嵌入式系统建立一个通用的Bootloader，不同的处理器架构都有不同的Bootloader，Bootloader不但依赖于cpu的体系结构，还依赖于嵌入式系统板级设备的配置。对于2块不同的板子而言，即使他们使用的是相同的处理器，要想让运行在一块板子上的Bootloader程序也能运行在另一块板子上，一般也需要修改Bootloader的源程序。 </div>
<p>&nbsp;</p>
<div><strong><font color=#0000ff>Bootloader的启动方式</font></strong> </div>
<p>&nbsp;</p>
<div>Bootloader的启动方式主要有网络启动方式、磁盘启动方式和Flash启动方式。 </div>
<p>&nbsp;</p>
<div><strong>1、网络启动方式</strong> </div>
<p align=center><a id=ImgSpan href="http://www.farsight.com.cn/FarsightBBS/UploadFile/2007-12/20071271619932958.gif" target=_blank><img alt=按此在新窗口浏览图片 src="http://www.farsight.com.cn/FarsightBBS/UploadFile/2007-12/20071271619932958.gif" onload="return imgzoom(this,550)" border=0></a></p>
<div><br></div>
<p align=center>图1&nbsp;&nbsp;Bootloader网络启动方式示意图
<p>&nbsp;</p>
<div>如图1所示，里面主机和目标板，他们中间通过网络来连接，首先目标板的DHCP/BIOS通过BOOTP服务来为Bootloader分配IP地址，配置网络参数，这样才能支持网络传输功能。我们使用的u-boot可以直接设置网络参数，因此这里就不用使用DHCP的方式动态分配IP了。接下来目标板的Bootloader通过TFTP服务将内核映像下载到目标板上，然后通过网络文件系统来建立主机与目标板之间的文件通信过程，之后的系统更新通常也是使用Boot Loader的这种工作模式。工作于这种模式下的Boot Loader通常都会向它的终端用户提供一个简单的命令行接口。 </div>
<p>&nbsp;</p>
<div><strong>2、磁盘启动方式</strong> </div>
<p>&nbsp;</p>
<div>这种方式主要是用在台式机和服务器上的，这些计算机都使用BIOS引导，并且使用磁盘作为存储介质，这里面两个重要的用来启动linux的有LILO和GRUB，这里就不再具体说明了。 </div>
<p>&nbsp;</p>
<div><strong>3、Flash启动方式</strong> </div>
<p>&nbsp;</p>
<div>这是我们最常用的方式。Flash有NOR Flash和NAND Flash两种。NOR Flash可以支持随机访问，所以代码可以直接在Flash上执行，Bootloader一般是存储在Flash芯片上的。另外Flash上还存储着参数、内核映像和文件系统。这种启动方式与网络启动方式之间的不同之处就在于，在网络启动方式中，内核映像和文件系统首先是放在主机上的，然后经过网络传输下载进目标板的，而这种启动方式中内核映像和文件系统则直接是放在Flash中的，这两点在我们u-boot的使用过程中都用到了。 </div>
<p>&nbsp;</p>
<div><strong><font color=#0000ff>U-boot的定义</font></strong> </div>
<p>&nbsp;</p>
<div>U-boot，全称Universal Boot Loader，是由DENX小组的开发的遵循GPL条款的开放源码项目，它的主要功能是完成硬件设备初始化、操作系统代码搬运，并提供一个控制台及一个指令集在操作系统运行前操控硬件设备。U-boot之所以这么通用，原因是他具有很多特点：开放源代码、支持多种嵌入式操作系统内核、支持多种处理器系列、较高的稳定性、高度灵活的功能设置、丰富的设备驱动源码以及较为丰富的开发调试文档与强大的网络技术支持。另外u-boot对操作系统和产品研发提供了灵活丰富的支持，主要表现在：可以引导压缩或非压缩系统内核，可以灵活设置/传递多个关键参数给操作系统，适合系统在不同开发阶段的调试要求与产品发布，支持多种文件系统，支持多种目标板环境参数存储介质，采用CRC32校验，可校验内核及镜像文件是否完好，提供多种控制台接口，使用户可以在不需要ICE的情况下通过串口/以太网/USB等接口下载数据并烧录到存储设备中去（这个功能在实际的产品中是很实用的，尤其是在软件现场升级的时候），以及提供丰富的设备驱动等。 </div>
<p>&nbsp;</p>
<div><strong><font color=#0000ff>u-boot源代码的目录结构</font></strong> </div>
<p>&nbsp;</p>
<div>1、board中存放于开发板相关的配置文件，每一个开发板都以子文件夹的形式出现。<br>2、Commom文件夹实现u-boot行下支持的命令，每一个命令对应一个文件。<br>3、cpu中存放特定cpu架构相关的目录，每一款cpu架构都对应了一个子目录。<br>4、Doc是文档目录，有u-boot非常完善的文档。<br>5、Drivers中是u-boot支持的各种设备的驱动程序。<br>6、Fs是支持的文件系统，其中最常用的是JFFS2文件系统。<br>7、Include文件夹是u-boot使用的头文件，还有各种硬件平台支持的汇编文件，系统配置文件和文件系统支持的文件。<br>8、Net是与网络协议相关的代码，bootp协议、TFTP协议、NFS文件系统得实现。<br>9、Tooles是生成U-boot的工具。 </div>
<p>&nbsp;</p>
<div>对u-boot的目录有了一些了解后，分析启动代码的过程就方便多了，其中比较重要的目录就是/board、/cpu、/drivers和/include目录，如果想实现u-boot在一个平台上的移植，就要对这些目录进行深入的分析。 </div>
<p>&nbsp;</p>
<div><strong><font color=#0000ff>u-boot的启动过程</font></strong> </div>
<p>&nbsp;</p>
<div>系统启动的入口点。既然我们现在要分析u-boot的启动过程，就必须先找到u-boot最先实现的是哪些代码，最先完成的是哪些任务。另一方面一个可执行的image必须有一个入口点，并且只能有一个全局入口点，所以要通知编译器这个入口在哪里。由此我们可以找到程序的入口点是在/board/lpc2210/u-boot.lds中指定的，其中ENTRY(_start)说明程序从_start开始运行，而他指向的是cpu/arm7tdmi/start.o文件。因为我们用的是ARM7TDMI的cpu架构，在复位后从地址0x00000000取它的第一条指令，所以我们将Flash映射到这个地址上，这样在系统加电后，cpu将首先执行u-boot程序。 </div>
<p>&nbsp;</p>
<div>u-boot的启动过程是多阶段实现的，分了两个阶段。依赖于cpu体系结构的代码（如设备初始化代码等）通常都放在stage1中，而且通常都是用汇编语言来实现，以达到短小精悍的目的。而stage2则通常是用C语言来实现的，这样可以实现复杂的功能，而且代码具有更好的可读性和可移植性。 </div>
<p>&nbsp;</p>
<div>下面我们先详细分析下stage1中的代码，如图2所示：</div>
<p align=center><a id=ImgSpan href="http://www.farsight.com.cn/FarsightBBS/UploadFile/2007-12/200712716213266446.gif" target=_blank><img alt=按此在新窗口浏览图片 src="http://www.farsight.com.cn/FarsightBBS/UploadFile/2007-12/200712716213266446.gif" onload="return imgzoom(this,550)" border=0></a></p>
<div align=center>图2&nbsp;&nbsp;Start.s程序流程 </div>
<p>&nbsp;</p>
<div align=center>代码真正开始是在_start，设置异常向量表，这样在cpu发生异常时就跳转到/cpu/arm7tdmi/interrupts中去执行相应得中断代码。在interrupts文件中大部分的异常代码都没有实现具体的功能，只是打印一些异常消息，其中关键的是reset中断代码，跳到reset入口地址。 </div>
<p>&nbsp;</p>
<div>reset复位入口之前有一些段的声明。在reset中，首先是将cpu设置为svc32模式下，并屏蔽所有irq和fiq。在u-boot中除了定时器使用了中断外，其他的基本上都不需要使用中断，比如串口通信和网络等通信等，在u-boot中只要完成一些简单的通信就可以了，所以在这里屏蔽掉了所有的中断响应。 </div>
<p>&nbsp;</p>
<div>初始化外部总线。这部分首先设置了I/O口功能，包括串口、网络接口等的设置，其他I/O口都设置为GPIO。然后设置BCFG0~BCFG3，即外部总线控制器。这里bank0对应Flash，设置为16位宽度，总线速度设为最慢，以实现稳定的操作；Bank1对应DRAM，设置和Flash相同；Bank2对应RTL8019。 </div>
<p>&nbsp;</p>
<div>接下来是cpu关键设置，包括系统重映射（告诉处理器在系统发生中断的时候到外部存储器中去读取中断向量表）和系统频率。 </div>
<p>&nbsp;</p>
<div>lowlevel_init，设定RAM的时序，并将中断控制器清零。这些部分和特定的平台有关，但大致的流程都是一样的。 </div>
<p>&nbsp;</p>
<div>下面就是代码的搬移阶段了。为了获得更快的执行速度，通常把stage2加载到RAM空间中来执行，因此必须为加载Boot Loader的stage2准备好一段可用的RAM空间范围。空间大小最好是memory page大小(通常是4KB)的倍数，一般而言，1M的RAM空间已经足够了。flash中存储的u-boot可执行文件中，代码段、数据段以及BSS段都是首尾相连存储的，所以在计算搬移大小的时候就是利用了用BSS段的首地址减去代码的首地址，这样算出来的就是实际使用的空间。程序用一个循环将代码搬移到0x81180000，即RAM底端1M空间用来存储代码。然后程序继续将中断向量表搬到RAM的顶端。由于stage2通常是C语言执行代码，所以还要建立堆栈去。在堆栈区之前还要将malloc分配的空间以及全局数据所需的空间空下来，他们的大小是由宏定义给出的，可以在相应位置修改。基本内存分布图：<br></div>
<p align=center><a id=ImgSpan href="http://www.farsight.com.cn/FarsightBBS/UploadFile/2007-12/20071271622022868.gif" target=_blank><img alt=按此在新窗口浏览图片 src="http://www.farsight.com.cn/FarsightBBS/UploadFile/2007-12/20071271622022868.gif" onload="return imgzoom(this,550)" border=0></a></p>
<div align=center><br><br>图3&nbsp;&nbsp;搬移后内存分布情况图 </div>
<p>&nbsp;</p>
<div>接下来是u-boot启动的第二个阶段，是用c代码写的，这部分是一些相对变化不大的部分，我们针对不同的板子改变它调用的一些初始化函数，并且通过设置一些宏定义来改变初始化的流程，所以这些代码在移植的过程中并不需要修改，也是错误相对较少出现的文件。在文件的开始先是定义了一个函数指针数组，通过这个数组，程序通过一个循环来按顺序进行常规的初始化，并在其后通过一些宏定义来初始化一些特定的设备。在最后程序进入一个循环，main_loop。这个循环接收用户输入的命令，以设置参数或者进行启动引导。 </div>
<p>&nbsp;</p>
<div>本篇文章将分析重点放在了前面的start.s上，是因为这部分无论在移植还是在调试过程中都是最容易出问题的地方，要解决问题就需要程序员对代码进行修改，所以在这里简单介绍了一下start.s的基本流程，希望能对大家有所帮助。</div>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/40174.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2008-02-26 14:51 <a href="http://www.cnitblog.com/zouzheng/articles/40174.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>U-BOOT分析</title><link>http://www.cnitblog.com/zouzheng/articles/40173.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Tue, 26 Feb 2008 06:49:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/40173.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/40173.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/40173.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/40173.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/40173.html</trackback:ping><description><![CDATA[我之前发在 linuxforum上面， 不过讨论的人比较少 ， 就贴在这里了。 <br>具体可见：
<p style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 150%">http://www.linuxforum.net/forum/showflat.php?Cat=&amp;Board=embedded&amp;Number=651003&amp;page=0&amp;view=collapsed&amp;sb=5&amp;o=0&amp;fpart=1#Post651082 <br><br>再读bootm源码，现象正常！ <br><br>呵呵 ， 原来 tftp download到是否是 hdr-&gt;ih_load(0x30008000) 确实是有说道的。 <br>原来对bootm源码有个地方理解错了， bootm 会根据 hdr-&gt;ih_comp 判断是否需要解压， 对于<br>mkimage -C none 的是不需要解压的。 <br><br>1&gt; mkimage -A arm -O linux -T kernel -C none -a 30008000 -e <br>30008040 -n linux-2.6.18.8 -d zImage uImage2.6.18.8-8040 <br>这种情况 ，只能把 uImage download到 30008000的位置上 ，否则 从 30008040<br>是启动不了的。 <br>2&gt; mkimage -A arm -O linux -T kernel -C none -a 30008000 -e <br>30008000 -n linux-2.6.18.8 -d zImage uImage2.6.18.8-8000 <br>这种情况download地址随便。 <br>如果 tftp 下载地址==0x30008000 ， 就从 0x30008040 启动就肯定OK 。 <br>详细的请看代码。 <br>研究了一下 u-boot-1.2.0 里面的 bootm的实现代码： do_bootm_linux() 函数 ，<br>原来由于我用mkimage的的时候的选项是 -C none ， 所以下面的判断中 hdr-&gt;ih_comp ＝ <br>IH_COMP_NONE <br>通过再读一遍代码， 也弄明白了上次问的问题， 就是 u－boot里面的解压和<br>内核自解压的区别： u-boot 里面的解压实际上是bootm 实现的 ， 把 mkimage -C bzip2<br>或者gzip 生成的 uImage进行解压 ； 而kernel的自解压是对zImage进行解压，<br>发生在bootm解压之后。 <br><br>呵呵 ， 原来 tftp download到是否是 hdr-&gt;ih_load(0x30008000) 确实是有说道的。 <br>原来对bootm源码有个地方理解错了， bootm 会根据 hdr-&gt;ih_comp 判断是否需要解压， 对于<br>mkimage -C none 的是不需要解压的。 <br><br>1&gt; mkimage -A arm -O linux -T kernel -C none -a 30008000 -e <br>30008040 -n linux-2.6.18.8 -d zImage uImage2.6.18.8-8040 <br>这种情况 ，只能把 uImage download到 30008000的位置上 ，否则 从 30008040<br>是启动不了的。 <br>2&gt; mkimage -A arm -O linux -T kernel -C none -a 30008000 -e <br>30008000 -n linux-2.6.18.8 -d zImage uImage2.6.18.8-8000 <br>这种情况download地址随便。 <br>如果 tftp 下载地址==0x30008000 ， 就从 0x30008040 启动就肯定OK 。 <br>详细的请看代码。 <br>研究了一下 u-boot-1.2.0 里面的 bootm的实现代码： do_bootm_linux() 函数 ，<br>原来由于我用mkimage的的时候的选项是 -C none ， 所以下面的判断中 hdr-&gt;ih_comp ＝ <br>IH_COMP_NONE <br>通过再读一遍代码， 也弄明白了上次问的问题， 就是 u－boot里面的解压和<br>内核自解压的区别： u-boot 里面的解压实际上是bootm 实现的 ， 把 mkimage -C bzip2<br>或者gzip 生成的 uImage进行解压 ； 而kernel的自解压是对zImage进行解压，<br>发生在bootm解压之后。 <br><br>switch (hdr-&gt;ih_comp) {<br>case IH_COMP_NONE:<br>if(ntohl(hdr-&gt;ih_load) == addr) { //如果你是tftp 到 0x30008000那么这里就命中了。 <br>printf (" XIP %s ... ", name); //tftp download 这里， 确实打印出来了。 <br>} else { //否则随便download到一个地址， 流程就进入这里了。 <br>#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG) //这个条件编译不成立<br>size_t l = len;<br>void *to = (void *)ntohl(hdr-&gt;ih_load); <br>void *from = (void *)data; <br>printf (" Loading %s ... ", name); <br>while (l &gt; 0) {<br>size_t tail = (l &gt; CHUNKSZ) ? CHUNKSZ : l;<br>WATCHDOG_RESET();<br>memmove (to, from, tail);<br>to += tail;<br>from += tail;<br>l -= tail;<br>}<br>#else/* !(CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG) */<br>------------------------实际执行的是这里---------------------------------<br>memmove ((void *) ntohl(hdr-&gt;ih_load), (uchar *)data, len); <br>//从这里就可以看出一旦 tftpdownload到一个任意地址， bootm 都会把<br>//它（去掉header后的kernel搬运到0x30008000 的位置上，因此entry <br>//point 肯定要是 0x30008000 ，但是对于tftp 恰好download到 <br>//0x30008000 的位置上的时候， 你会发现上面的代码中，就donothing <br>//了，因此就必须从 0x30008040 <br><br>#endif/* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */<br>}<br>break;<br>case IH_COMP_GZIP:<br>printf (" Uncompressing %s ... ", name);<br>if (gunzip ((void *)ntohl(hdr-&gt;ih_load), unc_len, // 把它解压到ih_load的位置上去<br>(uchar *)data, &amp;len) != 0) {<br>puts ("GUNZIP ERROR - must RESET board to recover\n");<br>SHOW_BOOT_PROGRESS (-6);<br>do_reset (cmdtp, flag, argc, argv);<br>}<br>break;<br>.....<br>省略了 gzip和 bzip2的处理 ， // 这里也说明 和 zImage的自解压是不一样的。 <br>gzip : mkimage -C gzip ; <br>bzip2 mkimage -C bzip2 . <br>他是只uImage 本身被压缩了 <br>default:<br>if (iflag)<br>enable_interrupts();<br>printf ("Unimplemented compression type %d\n", hdr-&gt;ih_comp);<br>SHOW_BOOT_PROGRESS (-7);<br>return 1;<br>}<br>//解压完毕，对于我得板子来说，是解压到 0x8000的位置上去了，这是个物理地址<br>puts ("OK\n");<br>SHOW_BOOT_PROGRESS (7);</p>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/40173.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2008-02-26 14:49 <a href="http://www.cnitblog.com/zouzheng/articles/40173.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>u-boot 分析 - &lt;节选&gt; [嵌入式Linux系统开发技术详解-基于ARM]</title><link>http://www.cnitblog.com/zouzheng/articles/40172.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Tue, 26 Feb 2008 06:45:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/40172.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/40172.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/40172.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/40172.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/40172.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 6.1 &nbsp;Bootloader对于计算机系统来说，从开机上电到操作系统启动需要一个引导过程。嵌入式Linux系统同样离不开引导程序，这个引导程序就叫作Bootloader。6.1.1&nbsp; Bootloader介绍Bootloader是在操作系统运行之前执行的一段小程序。通过这段小程序，我们可以初始化硬件设备、建立内存空间的映射表，从而建立适当的系统软硬件环境，为最终调...&nbsp;&nbsp;<a href='http://www.cnitblog.com/zouzheng/articles/40172.html'>阅读全文</a><img src ="http://www.cnitblog.com/zouzheng/aggbug/40172.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2008-02-26 14:45 <a href="http://www.cnitblog.com/zouzheng/articles/40172.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>QT技巧 </title><link>http://www.cnitblog.com/zouzheng/articles/39330.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Fri, 25 Jan 2008 13:03:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/39330.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/39330.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/39330.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/39330.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/39330.html</trackback:ping><description><![CDATA[Qt&amp;Kdevelop技巧集(原创)<br>发表：2004-1-21 13:39:23 出处：你的博客网(yourblog.org)<br>1． 如何在Qt程序中加入OpenGL支持。<br>在QT程序中加入OpenGL支持很简单，只需要在Kdevelop连接的库中加入&#8220;-lGL -lGLU&#8221;即可，如果需要glut支持，还可以加入&#8220;-lglut&#8221;。具体操作是在kdevelop集成编译环境中按下&#8221;F7&#8221;，在弹出的对话框中选择 &#8220;Linker&#8221;一项，在输入栏输入你想添加的库即可，写法与gcc/g++一致。<br>一般在类QGLWidget中使用OpenGL,调用此类的头文件是qgl.h,具体写法请参考qt例程中的gear,texture,box等程序(在RedHat7.2中,它们在/usr/lib/qt-2.3.1/doc/examples下).<br><br>2． 检验linux/Unix环境是否支持OpenGL.<br>Qt中的QGLFormat类可以帮助我们轻易检验系统是否支持OpenGL，载入头文件（#include &lt;qgl.h&gt;）后，我们就可以使用QGLFormat的静态函数hasOpenGL来检验，具体写法如下例：<br>if (!QGLFormat::hasOpenGL()) //Test OpenGL Environment<br>{<br>qWarning( "This system has no OpenGL support. Exiting." );//弹出警告对话框<br>return -1;<br>}<br><br>3.获得屏幕的高和宽.<br>一般我们可以通过QT的Qapplication类来获得系统的一些信息,载入头文件(#include &lt;qapplication.h&gt;)我们就可以调用它,下例是使主程序充满整个屏幕的代码:<br>Gui_MainForm gui_mainform;<br>a.setMainWidget( &amp;gui_mainform );<br>gui_mainform.resize( QApplication::desktop()-&gt;width(), QApplication::desktop()-&gt;height() ); gui_mainform.show();<br><br>4.关于信号和槽.<br>信号和槽机制是QT库的重要特性,可以说不了解它就不了解Qt.此机制能在各类间建立方便快捷的通信联系,只要类中加载了Q_OBJECT宏并用 connect函数正确连接在一起即可,具体写法这里就不赘述了.但本人在使用过程中发现使用此机制容易破坏程序的结构性和封装性,速度也不是很让人满意,尤其是在跨多类调用时.鄙人的一孔之见是: 信号和槽机制不可不用,但不可多用.<br><br>5.QT程序中界面的设计.<br>尽管Kdevelop是一个优秀的集成编译环境,可遗憾的是它不是一个可视化的编译环境,好在有Qdesigner来帮助我们完成界面设计,该程序的使用很简单,使用过VB,VC和Delphi的程序员能很快其操作方式,操作完成后存盘会生成一个扩展名为&#8221;ui&#8221;的文件,你接下来的任务就是把它解析成 cpp和h文件,假设文件名为myform.ui,解析方法如下:<br>$uic myform.ui &#8211;I myform.h &#8211;o myform..cpp //这句生成cpp文件<br>$uic myform.ui &#8211;o myform.h //这句生成h文件.<br><br>6.由pro文件生成Makefile.<br>对于Linux/Unix程序员来说编写Makefile文件是一项令人烦恼的任务,而qt程序员就没有这样的烦恼,一句$qmake &#8211;o Makefile myprogram.pro就可以轻松愉快的完成任务,而pro文件的编写也很容易,其核心是h和cpp文件的简单列表.具体写法请参考一下qt自带的样例和教程吧(在RedHat7.2中,它在/usr/lib/qt-2.3.1/doc/examples下),相对Makefile文件简直没有什么难度.<br><br>7.主组件的选择.<br>一般我们在编程是使用继承Qwidget类的类作为主组件,这当然未可厚非.但在制作典型的多文档和单文档程序时我们有更好的选择— QmainWindow类,它可以方便的管理其中的菜单工具条主窗口和状态条等,在窗体几何属性发生变化时也能完美的实现内部组件缩放,这比用传统的几何布局类来管理要方便得多,而且不用写什么代码.关于它的具体细节请查阅QT的帮组文档,这里就不赘述了.<br><br>8.菜单项中加入Checked项.<br>在QT中,菜单项中加入Checked有点麻烦,具体写法如下:<br>1&gt; 定义int型成员变量,并在创建菜单项中写:<br>displayGeometryMode=new QPopupMenu(this); //这里创建弹出菜单组displayGeometryMode<br>m_menuIDWire=displayGeometryMode-&gt;insertItem("Wire",this,SLOT(slt_Change2WireMode()));.//创建弹出菜单子项<br>displayGeometryMode-&gt;setItemChecked(m_ menuIDWire,true);//设定此子项为选择状态<br><br>2&gt; 再在槽函数中写:<br>displayGeometryMode-&gt;setItemChecked(m_menuIDWire,false);//这里设定此子项为非选择状态<br><br>9.截获程序即将退出的信号.<br>有些时候我们需要在程序即将退出时进行一些处理,如保存文件等等.如何截获程序退出的信号呢?还是要用到Qapplication类的aboutToQuit()信号,程序中可以这样写:<br>connect(qApp,SIGNAL(aboutToQuit()),this,SLOT(Slot_SaveActions()));<br>在槽函数Slot_SaveActions()就可以进行相关处理了,注意,使用全局对象qApp需要加载头文件(#include &lt;qapplication.h&gt;).<br><br>10.弹出标准文件对话框.<br>在程序中弹出文件对话框是很容易处理的,举例如下:<br>QString filter="Txt files(*.txt)\n" //设置文件过滤,缺省显示文本文件<br>"All files(*)" ; //可选择显示所有文件<br>QString Filepathname=QFileDialog::getOpenFileName(" ",filter,this);//弹出对话框,这句需要加载头文件(#include &lt; qfiledialog.h &gt;)<br><br><br>11.将当前日期时间转化为标准Qstring.<br>QDateTime currentdatetime =QDateTime::currentDateTime();//需要加载头文件(#include &lt; qdatetime.h &gt;)<br>QString strDateTime=currentdatetime.toString();<br><br>12.设置定时器<br>所有Qobject的子类在设置定时器时都不必加载一个Qtimer对象,因为这样造成了资源浪费且需要书写多余的函数,很不方便.最好的办法是重载timerEvent函数,具体写法如下:<br>class Gui_DlgViewCtrlDatum : public QDialog<br>{<br>Q_OBJECT<br>public:<br>Gui_DlgViewCtrlDatum( QWidget* parent = 0, const char* name = 0, bool modal = FALSE, WFlags fl = 0 );<br>~Gui_DlgViewCtrlDatum();<br>protected:<br>void timerEvent( QTimerEvent * );<br>};<br>void Gui_DlgViewCtrlDatum::timerEvent( QTimerEvent *e )<br>{<br>//statements<br>}<br>再在Gui_DlgViewCtrlDatum的构造函数中设置时间间隔:<br>startTimer(50);//单位为毫秒<br><br>这样,每隔50毫秒,函数timerEvent便会被调用一次.<br><br>13.最方便的几何布局类QGridLayout<br>在QT的几何布局类中,笔者认为QgridLayout使用最为方便,举例如下:<br>QGridLayout* layout=new QGridLayout(this,10,10);//创建一个10*10的QgridLayout实例<br>layout-&gt;addMultiCellWidget(gui_dlgslab_glwnd,1,8,0,7);//将OpenGL窗口固定在QgridLayout中的(1,0)单元格到(8,7)单元格中<br>layout-&gt;addMultiCellWidget(Slider1,0,9,8,8);//将一个slider固定在单元格(0,8)到(9,8)中<br>layout-&gt;addWidget(UpLimitLbl,1,9);//将一个label(UpLimitLbl)固定在单元格(1,9)中<br>这样,无论窗体大小如何改变,它们的布局方式都不会发生改变,这比反复使用QvboxLayout和QhboxLayout要方便快捷许多.<br>注:使用几何布局类需要调用头文件(#include &lt;qlayout.h&gt;)<br><br>14.字符串类Qstring和字符串链表类QstringList.<br>Qstring是Qt中标准字符串类,下面列出它的一些常用函数:<br>toInt():将字符串转化成int类型.<br>ToFloat():将字符串转化成float类型.<br>ToDouble():将字符串转化成double类型.<br>Left(n):从左起取n个字符<br>Right(n):从右起取n个字符<br>SetNum(n):将实数n(包括int,float,double等)转化为Qsting型.<br><br>QstringList是大家比较少使用的类,它可以看成Qstring组成的链表(QT中标准链表类Qlist的函数对它都适用,它的单个节点是Qstring类型的),特别适合与处理文本,下面一段代码就可见其方便快捷:<br>Qstring strtmp=&#8221;abc|b|c|d&#8221;;<br>QstringList strlsttmp;<br>Strlsttmp =QStringList::split("|", strtmp);<br>For(unsigned int I=0;I&lt; Strlsttmp.count();I++)<br>{<br>cout&lt;&lt; Strlsttmp.at(I);<br>}<br>结果输出为:abc b c d,也就是说,通过一个函数split,一行文本就被符号&#8221;|&#8221;自动分割成了单个字符串.这在文本处理时特别省力.(请参考c语言大全第四版中用&#8221;strtok&#8221;函数分割文本的例程,将双方比较一下)<br><br>15. QGLWidget类如何加入鼠标支持.<br>QGLWidget类加入鼠标支持需要重载以下函数:<br>void mousePressEvent(QMouseEvent*);<br>void mouseMoveEvent(QMouseEvent*);<br>void mouseReleaseEvent(QMouseEvent*);<br>请具体看一个实例:<br>class Gui_WgtMain_GLWnd : public QGLWidget {<br>Q_OBJECT<br>public:<br>Gui_WgtMain_GLWnd(QWidget *parent=0, const char *name=0);<br>~Gui_WgtMain_GLWnd();<br>protected:<br>void initializeGL();<br>void paintGL();<br>void resizeGL( int w, int h );<br>void mousePressEvent(QMouseEvent*);<br>void mouseMoveEvent(QMouseEvent*);<br>void mouseReleaseEvent(QMouseEvent*);<br>private:<br>int m_nCnt;<br>};<br>void Gui_WgtMain_GLWnd::mousePressEvent(QMouseEvent* e)<br>{<br>//statements<br>}<br>void Gui_WgtMain_GLWnd:: mouseMoveEvent (QMouseEvent* e)<br>{<br>//statements<br>}<br>void Gui_WgtMain_GLWnd:: mouseReleaseEvent (QMouseEvent* e)<br>{<br>//statements<br>}<br>其中, e-&gt;x();e-&gt;y();可以获得鼠标的位置, e-&gt;button()可以取得鼠标按键的状态(左中右键以及ctrl,alt,shift等组合键),灵活使用他们就可以在用鼠标操作OpenGL画面了.<br><br>16.由ui文件生成.h和.cpp文件<br>生成.cpp文件<br>$uic myform.ui -i myform.h -o myform.cpp<br><br>生成.h文件<br>$uic myform.ui -o myform.h<br>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/39330.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2008-01-25 21:03 <a href="http://www.cnitblog.com/zouzheng/articles/39330.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Arch Linux 中文化－字体设置</title><link>http://www.cnitblog.com/zouzheng/articles/39329.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Fri, 25 Jan 2008 13:01:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/39329.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/39329.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/39329.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/39329.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/39329.html</trackback:ping><description><![CDATA[<strong>前言 ：<br><br></strong>Arch Linux 是一个定制性很强的发行版，其设计的哲学决定了Arch Linux 不太可能像SUSE、Fedora、Mandriva等发行版一样预设了一套完善的字体配置。Arch Linux需要用户进行一定的定制，用户在定制、配置过程中能学到更多的知识，这也是Arch Linux的设计哲学之一。 <br><br><strong>一 .字体相关库的简介</strong><br><br><strong>1 . LibXft </strong><br><br>libXft库是为了给X应用程序提供一个能访问FreeType字体光栅化引擎和X渲染扩展的、便于使用的接口，鉴于FreeType没有提供配置和定制 字体的功能，Xft也担负了这一任务。Xft提供了新的字体命名约定、复杂而精密的字体匹配和选择机制，并对相关功能进行充分的抽象，从而使得一般应用程 序既能够从使用X渲染扩展的文本输出获得益处，又能在不支持这一扩展的X服务器上正常工作。<br><br>libXft 是画字函数库, 它使用 Fontconfig match 到了所要的字型之后, 来决定该如何画这些字。libXft 会看情况而决定要不要使用 core protocol 或 XRender 来画字。libXft 主要作GTK 2 （GTK &lt; 2.8.0）程序，QT 程序的画字函数库。 <br><br><strong>2 .Cairo</strong><br><br>一个支持多种输出的向量图形库,也就是说，cairo是种画图的工具库，他可以向多种设备上画图.可以输出到pdf,ps，xlib，XCB，win32，svg。基于GTK （ = &gt; 2.8.0)的程序用cairo 画字而不再使用libXft 。<br><br><strong>3 .Fontconfig</strong><br><br>Fontconfig 包含两个基本的模块，即读取XML文件并建立内部配置的配置模块和接受请求的字体样板并返回最接近所需字体的匹配模块。<br><br><strong>4 .Freetype</strong><br><br>Freetype FreeType库是一个完全免费 ( 开源 ) 的、高质量的且可移植的字体<br>引擎，它提供统一的接口来访问多种字体格式文件，包括TrueType, OpenType, Type1, CID, CFF, Windows FON/FNT,X11 PCF 等。支持单色位图 (homochromous bitmap) 、反走样位图（ anti-alias bitmap ）的渲染。 Freetype 是高度模块化设计的程序库。Freetype 提供 libXft／cairo 如何画字的信息，包括处理 anti-aliasing 或 hinting. 因此 freetype 的改变会影响到 libXft/cairo 画出来的字，而 Fontconfig 的改变会影响到 libXft／cario 如何去选字来画。 <br><br><strong>5 .Pango </strong><br><br>Pango 是个开源的整合到GTK+2的渲染国际化文本文件的库，pango 的名字是由 希腊语 "Pan" (&#928;&#945;&#957;; lit. all) + 日语 "Go" (語; lit. language)组合成。也就是All language，Pango负责多国语言文本(如一个文本文件中由英文，日文，中文等不同的语系组成）的渲染。<br><br><br><font size=3><strong>二 .基本概念</strong></font><br><br><strong>1. 点阵字体 与 矢量字体</strong><br><br><strong>点阵字体</strong>也叫位图字体，其中每个字形都以一组二维像素 信息表示。由于位图的原故，点阵字体很难进行缩放，特定的点阵字体只能清晰地显示在相应的字号下。但对于 12-16px 这样小的汉字，点阵字体常常比其它类型的字体在屏幕上更好的显示效果。常见的点阵字体有 bdf，pcf，fnt，hbf 等格式。 <br>对于常见的计算机操作系统，字体的显示算法需要一些字体的信息来优化屏幕显示效果，英文称为 hinting。汉字由于笔画复杂，所以 hinting 的方式与西文截然不同。使用在汉字字体中嵌入预先制作的点阵位图既可以有效地避免 hinting 算法带来的计算开销，同时屏幕上显示的汉字边缘清晰，易于阅读。下面就是使用点阵显示和不使用点阵显示的两个例子： <br><br><strong>未使用点阵中文的显示效果 </strong><br><img style="CURSOR: pointer" onclick=javascript:window.open(this.src); alt="" src="http://img156.imageshack.us/img156/9267/nohint7qk.png" onload="return imgzoom(this,550);" border=0> <br><br><strong>使用点阵中文的显效果</strong> <br><img style="CURSOR: pointer" onclick=javascript:window.open(this.src); alt="" src="http://img100.imageshack.us/img100/7366/hinting3ik.png" onload="return imgzoom(this,550);" border=0><br><br><strong>矢量字体</strong>中每一个字形是通过数学曲线来描述的，它包含了字形边界上的关键点，连线的导数信息等，字体的渲染引擎通过读取这些数学矢量，然后 进行一定的数学运算来进行渲染。这类字体的好处是字体可以无限放大而不产生变形。矢量字体主要包括 Type1 和 TrueType 等几类。<br><br><strong>2 .内嵌点阵字的字体 </strong><br><br>由于CJK字体的结构复杂，在小号字体只有使用点阵才能有清晰的显示效果，但是点阵字体很难进行缩，而且线条单一，在大号字体显示时就显得苍白无力。把点阵字体内嵌到矢量字体就能发挥两者的优点，并且也弥补了两者的不足。小号字体时清晰明朗，大号字体时字型饱满，形态丰富。常见的内嵌点阵字的字体有：北京中易电子公司宋体SimSun 、方正宋体（FZSongti）、文鼎PL上海宋（Uming）、Firefly的文鼎ＰＬ新宋（AR PL New Sung）等。<br><br><strong>3 . Hinting 与 Autohinter</strong><br><br>Hinting 用来最佳化字型显示的方法。由于屏幕的像素有限，向量字型的缩放需要有更多的考虑, 例如当一条线位在两个像素格子中间时, 该取左边的格子还是右边的格子? 如果这方面的控制没有做好，就常常会出现字型的衬线没有对齐，或是小字歪七扭八的情况。 Hinting 是额外的信息, 它告诉 renderer 该如何处理这些细节的部份，使得向量字在小字的时候能够好看。也因此 Hinting 是非常费时费人力的工作，TrueType 字型很多，但是有良好 Hinting 的字型不多。拙劣的 Hinting 就会让字变得很难看。<br><br>为了稍微改善这个问题，freetype 有 autohint 的功能，可以自动为没有 hint 的字型做 hinting 的工作。另外由于 TrueType 的 hinting 是有专利的，不能完全自由地使用， autohint 就不受这个限制。autohint 自然无法做得像人力的 hint 一样好，不过至少比没有 hint 要好些。话虽如此，对于许多笔划复杂的文字 (如中文) 目前 freetype 的 autohint 还做得不甚完美，而因为建立完整的 hinting 的难度，即使是英文字，原本就很高，内建有 hinting 的中文字型就少之又少了。所以常常有人抱怨中文字在屏幕上很难看，就是没有理想 hinting, 或者是使用了 autohinter 所造成的一些反效果。<br><br><strong>4 . AA（Antialiasing）</strong><br><br>Antialiasing 是将字体在后台先以数倍的大小来绘画，然后再缩成想要的大小，未满一格的格子用灰阶补点。<br><br>Antialiasing 会给人一种朦胧的感觉，习惯了Windows XP 下清晰，锐利的字体显示时，Antialiasing 会让人不太适应，会让用户觉得Linux下的字体显示不如Windows XP。但是，其实 Anti-aliasing是一种很先进的显示技术 ，当长时间显示器上阅读的时候，Windows XP 下的锐利的字体显示风格，会让眼睛更加容易疲劳，使用Antialiasing，字体的显示更加柔和，更加适合长时间的在显示器上阅读文档，减少眼睛的疲劳。<br><br>微软的下一代OS Windows Vista 也将用ClearType 作为默认的显示效果，来取传统的黑白，锐利的英文字体显示和点阵CJK 显示。看！ClearType是不是和Linux下的 Anti-aliasing 效果很相似。:-D<br><br><img style="CURSOR: pointer" onclick=javascript:window.open(this.src); alt="" src="http://www.qcode.org/linux/UploadPic/2007/11/23/20071123132233800.jpg" onload="return imgzoom(this,550);" border=0><br><img style="CURSOR: pointer" onclick=javascript:window.open(this.src); alt="" src="http://img156.imageshack.us/img156/3976/aaat5rq.png" onload="return imgzoom(this,550);" border=0><br><br><strong>Autohint+antialiasing</strong><br><br><strong><font size=3>三 .字体简介</font></strong><br><br><strong>1 自由(free)的英文字体</strong><br>itstream vera fonts,DejaVu fonts[community repo]，MS True Type core fonts 等，DejaVu fonts是在Bitstream vera fonts release 1.10 的基础上进行不断扩充的字体。 MS True Type core fonts包含了Arial， Courier New， Times New Roman， Verdana等系列字体的总称。 MS True Type core fonts 的licence 是EULA，可以合法的免费使用。<br><br><strong>2 自由中文字体</strong><br><br>ttf-arphic-ukai<br>ttf-arphic-uming<br>ttf-fireflysung<br><br><strong>3 不合版权的字体</strong><br><br>LingSong ：Tahoma+Simsun的&#8220;杂交&#8221;字体，Tahoma版权属于微软，Simsun字体的版权属于 北京中易电子公司。 <br><br>Vera Sans YuanTi：Bitstream Vera+simsun点阵+方正准圆+方正粗圆的&#8220;杂交&#8221;字体。<br><br><strong><font size=3>四 .设置实例</font></strong><br><br><strong>1 开启/关闭 AA ，Autohint 与 Hinting</strong><br><br>AA 与 Autohint/Hinting配搭的效果示范图：<br><br><strong>Hinting-only</strong><br><img style="WIDTH: 438px; CURSOR: pointer" onclick=javascript:window.open(this.src); alt="" src="http://img156.imageshack.us/img156/2356/hintingonly0lr.png" onload="return imgzoom(this,550);" border=0><br><br><strong>Autohint-only</strong><br><img style="WIDTH: 438px; CURSOR: pointer" onclick=javascript:window.open(this.src); alt=��ͼƬ�ѱ���С�������鿴ԭ��СͼƬ�� src="http://img156.imageshack.us/img156/9067/autohitonly4ss.png" width=500 onload="return imgzoom(this,550);" border=0><br><br><strong>Hinting+Antialiasing</strong><br><img style="WIDTH: 438px; CURSOR: pointer" onclick=javascript:window.open(this.src); alt="" src="http://img100.imageshack.us/img100/3121/hintingantialias5as.png" onload="return imgzoom(this,550);" border=0><br><br><strong>Autohint+Antialiasing</strong><br><img style="WIDTH: 437px; CURSOR: pointer" onclick=javascript:window.open(this.src); alt="" src="http://img100.imageshack.us/img100/8963/autohintantialias8sa.png" onload="return imgzoom(this,550);" border=0><br><br><br>下面的配置表示：全部的字体使用autohint 和 antialiasing，渲染度为 hintfull ，通常这是一个很好的全局默认设置。也可以根据个人喜好使用渲染度为hintslight。<br>
<div style="MARGIN: 5px 20px 20px 0px">
<div class=smallfont style="MARGIN-BOTTOM: 2px">代码:</div>
<pre class=alt2 style="BORDER-RIGHT: #c6c6c6 1px solid; PADDING-RIGHT: 4px; BORDER-TOP: #c6c6c6 1px solid; PADDING-LEFT: 4px; PADDING-BOTTOM: 4px; MARGIN: 0px; OVERFLOW: auto; BORDER-LEFT: #c6c6c6 1px solid; WIDTH: 640px; PADDING-TOP: 4px; BORDER-BOTTOM: #c6c6c6 1px solid; HEIGHT: 194px">
<div dir=ltr style="TEXT-ALIGN: left">       &lt;match target="font"&gt;                &lt;edit name="autohint"&gt;                        &lt;bool&gt;true&lt;/bool&gt;                &lt;/edit&gt;                &lt;edit name="hintstyle"&gt;                        &lt;const&gt;hintfull&lt;/const&gt;                &lt;/edit&gt;                &lt;edit name="antialias"&gt;                        &lt;bool&gt;true&lt;/bool&gt;                &lt;/edit&gt;        &lt;/match&gt;</div>
</pre>
</div>
下面的配置就是代表的是：小于16号的MS core fonts,关闭Antialiasing 和 Autohint，(使用hinting) 。可以根据个人喜欢调节字号的大小。<br>
<div style="MARGIN: 5px 20px 20px 0px">
<div class=smallfont style="MARGIN-BOTTOM: 2px">代码:</div>
<pre class=alt2 style="BORDER-RIGHT: #c6c6c6 1px solid; PADDING-RIGHT: 4px; BORDER-TOP: #c6c6c6 1px solid; PADDING-LEFT: 4px; PADDING-BOTTOM: 4px; MARGIN: 0px; OVERFLOW: auto; BORDER-LEFT: #c6c6c6 1px solid; WIDTH: 640px; PADDING-TOP: 4px; BORDER-BOTTOM: #c6c6c6 1px solid; HEIGHT: 498px">
<div dir=ltr style="TEXT-ALIGN: left">        &lt;match target="font"&gt;                &lt;test name="family"&gt;                        &lt;string&gt;Andale Mono&lt;/string&gt;                        &lt;string&gt;Arial&lt;/string&gt;                        &lt;string&gt;Comic Sans MS&lt;/string&gt;                        &lt;string&gt;Georgia&lt;/string&gt;                        &lt;string&gt;Impact&lt;/string&gt;                        &lt;string&gt;Trebuchet MS&lt;/string&gt;                        &lt;string&gt;Verdana&lt;/string&gt;                        &lt;string&gt;Courier New&lt;/string&gt;                        &lt;string&gt;Times New Roman&lt;/string&gt;                        &lt;string&gt;Tahoma&lt;/string&gt;                        &lt;string&gt;Webdings&lt;/string&gt;                        &lt;string&gt;Albany AMT&lt;/string&gt;                        &lt;string&gt;Thorndale AMT&lt;/string&gt;                        &lt;string&gt;Cumberland AMT&lt;/string&gt;                        &lt;string&gt;Andale Sans&lt;/string&gt;                        &lt;string&gt;Andy MT&lt;/string&gt;                        &lt;string&gt;Bell MT&lt;/string&gt;                        &lt;string&gt;Monotype Sorts&lt;/string&gt;                &lt;/test&gt;&lt;test name="pixelsize" compare="less_eq"&gt;&lt;double&gt;16&lt;/double&gt;&lt;/test&gt;                &lt;edit name="autohint"&gt;                        &lt;bool&gt;false&lt;/bool&gt;                &lt;/edit&gt;                &lt;edit name="antialias"&gt;                        &lt;bool&gt;false&lt;/bool&gt;                &lt;/edit&gt;        &lt;/match&gt;</div>
</pre>
</div>
<br><strong>2 英文字和中文字等宽</strong><br><br>TrueType 字体分成两种，一种是可变字距，也就是每个字符宽度不一样，比如 "x" 与 "i"，这两个字符就不等宽，它是在每个字符中记录该字符的宽度，大多数的 TrueType 字体都是这种格式。 <br><br>另一种就是固定字距。也就是每个字符宽度都一样，这样显示或打印時，用会有对齐效果，比较美观，在英语环境中，不会有任何问题，但是CJK 的等宽字体，包含『半角英数字符』与『全角 CJK 字符』，字体引擎会把全角字宽度套用在半角字上，造成半宽字看起来间距太大。 <br><br>
<div style="MARGIN: 5px 20px 20px 0px">
<div class=smallfont style="MARGIN-BOTTOM: 2px">代码:</div>
<pre class=alt2 style="BORDER-RIGHT: #c6c6c6 1px solid; PADDING-RIGHT: 4px; BORDER-TOP: #c6c6c6 1px solid; PADDING-LEFT: 4px; PADDING-BOTTOM: 4px; MARGIN: 0px; OVERFLOW: auto; BORDER-LEFT: #c6c6c6 1px solid; WIDTH: 640px; PADDING-TOP: 4px; BORDER-BOTTOM: #c6c6c6 1px solid; HEIGHT: 226px">
<div dir=ltr style="TEXT-ALIGN: left">&lt;match target="font"&gt;                &lt;test target="pattern" name="lang" compare="contains"&gt;                        &lt;string&gt;zh&lt;/string&gt;                        &lt;string&gt;ja&lt;/string&gt;                        &lt;string&gt;ko&lt;/string&gt;                &lt;/test&gt;                &lt;edit name="spacing"&gt;                        &lt;const&gt;proportional&lt;/const&gt;                &lt;/edit&gt;                &lt;edit name="globaladvance"&gt;                &lt;bool&gt;false&lt;/bool&gt;                &lt;/edit&gt;        &lt;/match&gt;</div>
</pre>
</div>
这个配置表示：让字体引擎(FreeType)取消CJK字体的指定宽度。<br><br><strong>3 字体替换</strong><br><br>一些网页编写时候，对使用的字体预先设定，（如英文部分使用了simsun，使得英文显示很不美观）所以可以使用字体替换使得字体显示更美观。<br><br>把Simsun ， SimSun-18030 ， AR PL ShanHeiSun Uni等这个几个中文字体的英文部分，用 Tahoma,Arial 等替换。<br><br>
<div style="MARGIN: 5px 20px 20px 0px">
<div class=smallfont style="MARGIN-BOTTOM: 2px">代码:</div>
<pre class=alt2 style="BORDER-RIGHT: #c6c6c6 1px solid; PADDING-RIGHT: 4px; BORDER-TOP: #c6c6c6 1px solid; PADDING-LEFT: 4px; PADDING-BOTTOM: 4px; MARGIN: 0px; OVERFLOW: auto; BORDER-LEFT: #c6c6c6 1px solid; WIDTH: 640px; PADDING-TOP: 4px; BORDER-BOTTOM: #c6c6c6 1px solid; HEIGHT: 274px">
<div dir=ltr style="TEXT-ALIGN: left">&lt;match target="pattern"&gt;&lt;test name="family"&gt;&lt;string&gt;SimSun&lt;/string&gt;&lt;string&gt;SimSun-18030&lt;/string&gt;&lt;string&gt;AR PL ShanHeiSun Uni&lt;/string&gt;&lt;string&gt;AR PL New Sung&lt;/string&gt;&lt;string&gt;MingLiU&lt;/string&gt;&lt;/test&gt;&lt;edit binding="strong" mode="prepend" name="family"&gt;&lt;string&gt;Tahoma&lt;/string&gt;&lt;string&gt;Arial&lt;/string&gt;&lt;string&gt;Verdana&lt;/string&gt;&lt;string&gt;DejaVu Sans&lt;/string&gt;&lt;string&gt;Bitstream Vera Sans&lt;/string&gt;&lt;/edit&gt;&lt;/match&gt;</div>
</pre>
</div>
<br><br>把 AR PL ShanHeiSun Uni ， AR PL New Sung 字体中12～16号的中文字用 WenQuanYi Bitmap Song 点阵字替换， WenQuanYi Bitmap Song 比 AR PL ShanHeiSun Uni ， AR PL New Sung中的内嵌点阵更加完善，美观。<br><br>
<div style="MARGIN: 5px 20px 20px 0px">
<div class=smallfont style="MARGIN-BOTTOM: 2px">代码:</div>
<pre class=alt2 style="BORDER-RIGHT: #c6c6c6 1px solid; PADDING-RIGHT: 4px; BORDER-TOP: #c6c6c6 1px solid; PADDING-LEFT: 4px; PADDING-BOTTOM: 4px; MARGIN: 0px; OVERFLOW: auto; BORDER-LEFT: #c6c6c6 1px solid; WIDTH: 640px; PADDING-TOP: 4px; BORDER-BOTTOM: #c6c6c6 1px solid; HEIGHT: 274px">
<div dir=ltr style="TEXT-ALIGN: left">&lt;match target="pattern"&gt;&lt;test name="family" qual="any"&gt;&lt;string&gt;AR PL ShanHeiSun Uni&lt;/string&gt;&lt;string&gt;AR PL New Sung&lt;/string&gt;&lt;/test&gt;&lt;test compare="more_eq" name="pixelsize" &gt;&lt;double&gt;12&lt;/double&gt;&lt;/test&gt;&lt;test compare="less_eq" name="pixelsize" &gt;&lt;double&gt;16&lt;/double&gt;&lt;/test&gt;&lt;edit name="family" mode="prepend" binding="strong"&gt;&lt;string&gt;WenQuanYi Bitmap Song&lt;/string&gt;&lt;/edit&gt;&lt;/match&gt;</div>
</pre>
</div>
<br><br><strong>4 使用内嵌字体。</strong><br>
<div style="MARGIN: 5px 20px 20px 0px">
<div class=smallfont style="MARGIN-BOTTOM: 2px">代码:</div>
<pre class=alt2 style="BORDER-RIGHT: #c6c6c6 1px solid; PADDING-RIGHT: 4px; BORDER-TOP: #c6c6c6 1px solid; PADDING-LEFT: 4px; PADDING-BOTTOM: 4px; MARGIN: 0px; OVERFLOW: auto; BORDER-LEFT: #c6c6c6 1px solid; WIDTH: 640px; PADDING-TOP: 4px; BORDER-BOTTOM: #c6c6c6 1px solid; HEIGHT: 98px">
<div dir=ltr style="TEXT-ALIGN: left">&lt;match target="font"&gt;&lt;edit name="embeddedbitmap" mode="assign"&gt;&lt;bool&gt;true&lt;/bool&gt;&lt;/edit&gt;&lt;/match&gt;</div>
</pre>
</div>
内嵌点阵字当字体有内嵌字体时，优先使用内嵌字体。<br>注意：libXft 还没有embeddedbitmap选项的支持。可以到这里<a href="https://bugzilla.redhat.com/bugzilla/attachment.cgi?id=124032" target=_blank><font face=宋体 color=#000000>https://bugzilla.redhat.com/bugzilla....cgi?id=124032</font></a><br>得到libXft2.1.8.2的embeddedbitmap patch，通过ABS重新编译安装libXft 。<br><br><br><strong>5 必须使用hinting的字体</strong><br><br>有些 CJK 字体要the byte code interpreter （hinting）才能正确的显示，因为这些字体使用了hinting的一些技术制作的。常见的MingLiu 就是这类字体。<br>
<div style="MARGIN: 5px 20px 20px 0px">
<div class=smallfont style="MARGIN-BOTTOM: 2px">代码:</div>
<pre class=alt2 style="BORDER-RIGHT: #c6c6c6 1px solid; PADDING-RIGHT: 4px; BORDER-TOP: #c6c6c6 1px solid; PADDING-LEFT: 4px; PADDING-BOTTOM: 4px; MARGIN: 0px; OVERFLOW: auto; BORDER-LEFT: #c6c6c6 1px solid; WIDTH: 640px; PADDING-TOP: 4px; BORDER-BOTTOM: #c6c6c6 1px solid; HEIGHT: 210px">
<div dir=ltr style="TEXT-ALIGN: left">        &lt;match target="font"&gt;                &lt;test name="family"&gt;                        &lt;string&gt;MingLiU&lt;/string&gt;                        &lt;string&gt;PMingLiU&lt;/string&gt;                &lt;/test&gt;                &lt;edit name="autohint"&gt;                        &lt;bool&gt;false&lt;/bool&gt;                &lt;/edit&gt;                &lt;edit name="hinting"&gt;                        &lt;bool&gt;true&lt;/bool&gt;                &lt;/edit&gt;        &lt;/match&gt;</div>
</pre>
</div>
<br><br><strong>附：</strong><br><br><strong>Arch WiKi 的连接:</strong><br><br><a href="http://wiki.archlinux.org/index.php/字体设置" target=_blank><font face=宋体 color=#000000>http://wiki.archlinux.org/index.php/字体设置</font></a><br>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/39329.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2008-01-25 21:01 <a href="http://www.cnitblog.com/zouzheng/articles/39329.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>嵌入式Linux 的safe mode 设计与实现</title><link>http://www.cnitblog.com/zouzheng/articles/39328.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Fri, 25 Jan 2008 12:53:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/39328.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/39328.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/39328.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/39328.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/39328.html</trackback:ping><description><![CDATA[&nbsp;
<blockquote>目前的各种嵌入式产品已经丰富多彩，它们正改变着我们的生活方式。随着嵌入式产品功能的增加，如何让用户对已购买的产品的升级能安全地、顺利地完成，避免升级过程中出现的意外掉电所引起的产品故障，这样的问题要求嵌入产品设计开发者在设计时就将产品的 safe mode 安全模式考虑进去。这里我们将以一个嵌入式Linux 网络播放器为例，来说明 safe mode 安全模式的设计与实现。通过本文，我们可以了解到针对一个实际的嵌入式系统，设计中需要注意的技术要点和实现细节。</blockquote><!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--end RESERVED FOR FUTURE USE INCLUDE FILES-->
<p><a name=N1007D><span class=atitle>为什么需要 safe mode（安全模式）</span></a></p>
<p>当用户购买一个产品后，在后续的服务中，可能还会发生一些费用，让产品开发商增加成本，如免费电话咨询，产品的维修、寄送。所以说将产品的卖出并不意味着最终的赢利。这样的情况下，产品的设计就需要更加合理，更加优化，来满足用户各种可能的需求。特别是在发生异常故障的时候，如果能引导客户自行完成诊断、修复，那么将大大降低后续的服务成本。正因为如此，产品故障时，就很需要safe mode安全模式来帮助用户完成恢复的工作。</p>
<p>从节约产品的成本、产品所能提供的功能上来看，safe mode 是大有裨益的。</p>
<p>大家所熟知的 windows 系统，也提供了 safe mode 安全模式，它就可以帮助用户解决系统不稳定，硬件冲突等诸多故障，让用户在自己可以操作的能力范围内先行对系统进行诊断与修复。在很大程度上， windows 的 safe mode 给用户与 Microsoft 都带来了很大的便利。</p>
<p>嵌入式Linux产品与其他IT产品不同的地方，主要是使用flash来存贮运行时的系统。它没有大的内存，没有大的存储空间，但它却也是一个完整的系统。</p>
<p>在通常情况下，嵌入式Linux产品的flash上的内容是不会被破坏的，也即它们会有着较好的稳定性，不会因为用户的常规使用而导致flash上的 firmware被破坏。但随着产品的更新升级，用户也需要在自己家中完成对已购买商品的更新换代。而用户大多属于非技术熟悉者，在更新升级中就可能出现种种意想不到的情况。</p>
<p>比如在用户做firmware升级更新时，平时不会出现问题的firmware可能在这个过程中，就面临着巨大的风险，极有可能致使用户的系统无法启动，不能正常工作。这样的情况是我们不愿意看到的，而实际中却的的确确可能会发生。</p>
<p>考虑这样一个场景：当用户对产品进行firmware升级时，如果在烧写flash的过程中，意外掉电，那么用户手中的产品就将无法再次启动，因为 rootfs系统已经被破坏了。用户所能做的，也只能将产品送回产商进行维修。这样来回的过程不仅耗费用户的精力，同样也会增加产品开发商的成本。在产品升级换代很快的当前市场情况下，这样的情况可能会经常发生。</p>
<p>如何避免这样的情况的发生呢？如果我们可以提供一个机制，在进行升级前即往flash中写入一个标记，正常完成后，再写入另一个标记来表示整个过程的正常结束，否则的话，烧写时掉电不会写入第二个标记，只有第一个标记，那么就认为产品故障，这个时候，进入另一个新的提示界面，让用户自己选择从 USB或FTP来重新升级firmware。这样的话，整个过程用户就完全可以在界面的友好提示下自己完成，方便了用户与产品开发商。</p>
<p><a name=N1009C><span class=atitle>系统架构</span></a></p>
<p>本文以一个实际的产品为例，来说明safe mode的设计。</p>
<span style="DISPLAY: none">DeQLinux联盟</span><br><a name=fig1><strong>系统架构</strong></a><span style="DISPLAY: none">DeQLinux联盟</span><br><img height=352 alt=系统架构 src="http://www.xxlinux.com/linux/d/file/article/development/embed/2007-09-06/a9591e6a5dfef4c9af1f1a62df8ceb8e.jpg" width=364> <span style="DISPLAY: none">DeQLinux联盟</span><br>
<p>本系统为一个嵌入式Linux网络播放器，主要的功能为播放家庭网络中的多媒体文件，在家庭客厅等环境中有着大量的应用，它可以给用户提供更方便快捷的媒体文件的播放方式，并能充分利用家庭音响系统的巨大功能，而非PC环境下有限的外部设备，大大改善了媒体文件的播放体验。</p>
<span style="DISPLAY: none">DeQLinux联盟</span><br><a name=fig2><strong>本系统的架构如下图：</strong></a><span style="DISPLAY: none">DeQLinux联盟</span><br><img height=257 alt=本系统的架构如下图： src="http://www.xxlinux.com/linux/d/file/article/development/embed/2007-09-06/87170e46edfa1f09a7c5a275420c2975.jpg" width=481> <span style="DISPLAY: none">DeQLinux联盟</span><br>
<p>产品所使用的flash总大小为16M。</p>
<p>系统包括三大部分，即bootloader，config, kernel + rootfs：</p>
<span style="DISPLAY: none">DeQLinux联盟</span><br><img height=113 src="http://www.xxlinux.com/linux/d/file/article/development/embed/2007-09-06/e07bda036bc483e0d778e640b5d36316.jpg" width=532> <span style="DISPLAY: none">DeQLinux联盟</span><br>
<p>另外，/dev/mtdblock/0，在系统中对应整个flash block，即整个16M空间。</p>
<p>系统启动时，bootloader将kernel和根文件映象从flash上读取到RAM空间中，为内核设置启动参数，调用内核，进入application，进行媒体文件的播放。</p>
<p>这个通常意义上的嵌入式Linux系统，它是不带safe mode安全模式的。</p>
<p>这样的系统，在做系统更新升级时，主要是对kernel+rootfs部分进行升级，以此来增加系统的功能。</p>
<p>升级时，application主要是操作/dev/mtdblock/3设备文件：</p>
<p>第一步：下载新的firmware到ramfs中，也即ram disk中，比如/tmp目录下，采用的更新方式可以是USB或FTP；</p>
<p>第二步：read /tmp/firmware文件，并write到设备文件/dev/mtdblock/3上，即对已有的firmware进行了更新。</p>
<p>在升级的过程中，我们会提供友好的界面给用户，来提示下载进度与烧写flash的进度，让用户可以看到正在发生的状况。</p>
<p>最后烧写完成后，重新启动系统，即可进入到新的firmware中。</p>
<p>在通常的更新中，用户的产品配置config一般不去修改，保持用户已经做的配置选项，不能破坏。Config内容对应为/dev/mtdblock/2设备文件。</p>
<p>从USB/FTP 上更新时，所使用的firmware文件需要是一个更加完整的image文件，可以包括bootloader, default config, kernel+rootfs，并让application可以做到视image中的标记来决定是否需要更新bootloader、config等内容，这样会更加灵活。</p>
<p>在更新firmware时，如果掉电，那么kernel + rootfs部分将会出现不完整的情况，也就是说只写入了部分内容，而中途中断了，这样的话，一个不完整的系统将无法正常工作。在这样的情况下就需要safe mode安全模式了。</p>
<p><a name=N100FF><span class=atitle>safe mode架构设计</span></a></p>
<p>Safe mode的设计中，对原来的系统增加了两个部分的内容：</p>
<p>kernel + rootfs，即简单的UI界面与功能；</p>
<p>magic number，即烧写flash的标记。</p>
<span style="DISPLAY: none">DeQLinux联盟</span><br><img height=333 src="http://www.xxlinux.com/linux/d/file/article/development/embed/2007-09-06/98a10a6fd80a0f74e4fa150059c7227a.jpg" width=532> <span style="DISPLAY: none">DeQLinux联盟</span><br>
<p>safe mode实际上也是一个kernel + rootfs部分，只是它所具有的功能只包括一些简单的界面，主要是提供网络设置，从USB/FTP下载firmware，完成对flash的烧写。</p>
<p>为了区分，这里，将主功能部分的kernel + rootfs称为master。</p>
<p>我们将safe mode存放在master的后部，预留的flash大小为4M。</p>
<p>Magic number只占用一个字节的大小，是在这4M的最后的部分的一个字节，也即原始系统的15872K的最后一个字节位置处。</p>
<p>在开始烧写flash前，将magic number设置为0x55，表示烧写的开始。烧写正常结束后，将magic number设置为0xAA，表示烧写正常结束。</p>
<p>如果新产品中具备了safe mode模式，那么在以后再次更新升级时，开始烧写flash时，magic number的位置将会有0x55标记，如果烧写中途掉电，在重新启动后，将由bootloader来检查magic number的值，如果内容为0x55，那么bootloader将从safemode部分读出kernel和根文件映象，再为内核设置启动参数，调用内核，进入safe mode application。</p>
<p>如果bootloader读到magic number为0xAA，那么说明master firmware是正常的，就将直接进入master。</p>
<p>所以涉及到safe mode的地方也包括了对bootloader的修改，需要在系统上电阶段也检查safe mode的magic number，这个过程是必不可少的，只有在启动阶段就检查magic number，才能跳过损坏的master系统，进入安全模式，达到恢复系统的目的。</p>
<p><a name=N10135><span class=atitle>safe mode架构实现</span></a></p>
<p>在safe mode的实现中，需要保持原有master部分的稳定，所以对master系统的building system不做大的改动，也就是保持safe mode的building system与master的building system共存。原则上来说，要避免对master系统带来大的冲突。</p>
<p>Master building system主要涉及到的编译过程为：</p>
<p>make</p>
<p>make rootfs</p>
<p>这个时候将得到master.bin</p>
<p>safe mode building system和其类似，只是make rootfs部分有所区分：</p>
<p>make </p>
<p>make smrootfs</p>
<p>这个时候将得到safemode.bin</p>
<p>最后再将master与safe</p>
<p>mode部分做一个合并，得到一个整的rootfs</p>
<p>make dualrootfs</p>
<p>make dist</p>
<p>make</p>
<p>dualrootfs将调用一个外部的程序make_dual.c，所做的事情是要得到一个15872K的rootfs。这个rootfs包含的内容为master.bin + safemode.bin。</p>
<p>本系统中一般master.bin的大小约为10000K，再加上safemode.bin的4M，总大小并未达到15872K，那么中间多出的部分，我们需要将其补０填充好。需要补充的０的大小约为15872-4*1024-10000=1776K</p>
<span style="DISPLAY: none">DeQLinux联盟</span><br><img height=139 src="http://www.xxlinux.com/linux/d/file/article/development/embed/2007-09-06/feed70435aed3bc2973d76da1e2a6947.jpg" width=232> <span style="DISPLAY: none">DeQLinux联盟</span><br>
<p>make_dual.c就是完成上面的合并，补０的工作。它read master.bin，write rootfs，然后write　1776K个零到rootfs中，接下来read safemode.bin，再继续write 到rootfs中。</p>
<p>这样就得到了完整的、带master与safe mode的rootfs。</p>
<p><a name=N10180><span class=atitle>safe mode实现中遇到的问题及其解决</span></a></p>
<p><a name=N10187><span class=smalltitle>体积限制：</span></a></p>
<p>在safe mode的开发中，首先遇到的一个问题就是如何从已有的系统中简化出一个safe mode的application环境。</p>
<p>对master原有系统的裁剪来得到safe mode，将会比较容易，如果从头另写一套，将会花费较大精力，稳定性也无法得到确实的保障，所以最终采用的是精简master的系统来得到safe mode的大框架。</p>
<p>在实现safe mode时，要做的工作的原则是做到safe mode的rootfs尽量小，低于４M，并且保持与master外围特性的一致，这样可以避免重复开发，同时代码的共用可以减少维护的不便，提高整个系统的灵活度、稳定度。</p>
<p>就一个能运行的嵌入系统来说，最基本的内容应该包括Linux kernel，busybox工具包、图形驱动等内容。</p>
<p>在本系统中，为了支持FTP下载，需要有network的支持，也即需要包括wired/wireless的支持。</p>
<p>为了支持USB下载方式，就需要USB monitor管理进程的支持，这个主要是保持了与master系统的一致，而没有另外去写一个体积更小的USB管理模块。</p>
<p><a name=N1019F><span class=smalltitle>wireless模块：</span></a></p>
<p>本来在设计时，可以考虑不加入wireless的支持，但为了更加方便用户，保持用户的使用习惯，我们还是加入了对wireless的支持，这样也保持了与master系统的一致，但支持的代价是，safe mode的体积增大了大约250K。</p>
<p>在wireless module中，做了一个优化，master系统中wireless module在insmod时，是使用的rootfs中的/lib/module/wireless/XXX.o，这些未压缩的.o文件在rootfs系统中将占用较大空间，这样一来，对应的safe mode的内容将会超出４M的大小。为了解决这个问题，我们将这些wireless module压缩成wireless.tar.gz文件，放置到safemode.bin中，在Linux启动时，在/etc/rc脚本中将 wireless.tar.gz解压缩到ramfs中即／tmp/lib/module/wireless下，然后再从这里insmod安装 wireless模块。这样所做的努力，wireless module从原来的790K，缩减到了250K，而功能保持了一致。</p>
<p><a name=N101AB><span class=smalltitle>字体：</span></a></p>
<p>master 系统的字体使用的是freetype2，字体文件arialbd.ttf大约为280K，这也将占用大量的空间。由于safe mode在显示界面方面没有过高的要求，能让用户看到基本的图形界面就已经达到目的了，所以在safe mode中需要将freetype去掉。但由于master模式与safe mode都使用相同的图形引擎，这样就导致了，如果在safe mode中去掉freetype，那么就需要再次重新build基础的图形库，这样在master与safe mode的单独编译过程中就需要反复去make clean这些库。这会给每次的编译带来很大的不便，每次make clean等操作会占用大量的时间，耗时耗力。</p>
<p>基于这个考虑，我们决定master与safe mode在编译过程中都使用相同的图形库，即都编译生成freetype库。但在运行时，safe mode不去使用freetype。也就是说，freetype库会被编译进来，但字体文件不需要加到safe mode中，这样做的代价就是编译出来的safe mode的application比完全无freetype库的情况要大100K左右，但却保持了与master相同的库结构，而freetype字体就不再需要了，也就节约出了大约280K的空间。</p>
<p>最终优化的结果，safe mode的4M，包括Linux kernel, buzybox, safe mode application等压缩后的大小：</p>
<span style="DISPLAY: none">DeQLinux联盟</span><br><a name=fig6><strong>优化结果</strong></a><span style="DISPLAY: none">DeQLinux联盟</span><br><img height=220 alt=优化结果 src="http://www.xxlinux.com/linux/d/file/article/development/embed/2007-09-06/b08d1d37ea4230cf70104810874c2b0d.jpg" width=531> <span style="DISPLAY: none">DeQLinux联盟</span><br>
<p><a name=N101C9><span class=smalltitle>后续版本的兼容：</span></a></p>
<p>在safe mode的设计中，对后续多个版本升级的支持也是一个需要仔细考虑的地方。因为后续版本会存在很多的不确定性，如果发出的版本不能很好地兼容后续版本，那么将会给产品带来巨大的风险。</p>
<p>后续版本的可能情况，主要分两种：结构分区变化不大，结构分区变化巨大。</p>
<p>对后续版本中变化不大的情况，也即类似master + safe mode的情况，当再次更新时，只需要操作/dev/mtdblock/3对应master，/dev/mtdblock/4对应safe mode，即可。</p>
<p>但如果后续版本变化非常大，那么就需要特别注意了。</p>
<p>可以考虑这样一个情况：如果后续的版本，需求发生了大的变化，比如需要将原来master所在的分区再分成多个分区：</p>
<span style="DISPLAY: none">DeQLinux联盟</span><br><a name=fig7><strong>后续版本需求变化</strong></a><span style="DISPLAY: none">DeQLinux联盟</span><br><img height=352 alt=后续版本需求变化 src="http://www.xxlinux.com/linux/d/file/article/development/embed/2007-09-06/49a2706bf11ed3d5bc98de23dd8d5527.jpg" width=488> <span style="DISPLAY: none">DeQLinux联盟</span><br>
<p>那么从老版本升级到新版本时，这些分区的内容如何保证烧写后能正常工作呢？</p>
<p>解决的办法就是在老版本中，将后续的rootfs部分作为一个整体来操作，也就是说烧写时，是将master + part1 + part2+ safe mode作为一个整体来对待。在老版本看来，新版本中的这15872K的内容，不管它其中有多少个不同的分区，还是master + safe mode。在烧写时，还是按/dev/mtdblock/3对应master，/dev/mtdblock/4对应safe mode的方式来烧写，完成将15872K的内容完整烧写进flash即可。</p>
<p>为了做到这一点，在烧写中，我们将全部的15872K的内容分成两段，第一段为15872-4*1024=11776K，需要将其write到/dev/mtdblock/3中，第二段为4M，需要将其write到/dev/mtdblock/4中。这样全部的15872K的内容就完整地烧写完，而再次启动后的kernel会分辨出 master + part1 + part2 + safe mode，它们的总大小依然保持15872K不变。这整个过程中，都不用去理会新版本中到底包括哪些内容，哪些分区，只要保证是将15872K的内容全部完整地烧写进去就可以了。</p>
<p>整体rootfs的设计思想在这里帮了一个大忙，简化了升级更新时所需要考虑的复杂度，使设计变得更加灵活与易于维护。</p>
<p>这样才新发布的firmware里，如果分为多个分区，那么就保证再次升级时，将15872K的内容分成多段，写到类似/dev/mtdblock/3、4、5、6这样的设备文件里就可以了，只要保证这些区域是连续的、并且烧写的内容是全部的那15872K内容即可。</p>
<p><a name=N101FC><span class=smalltitle>Magic number：</span></a></p>
<p>值得注意的是，随着不同的版本的变化，magic number的位置还是应该保持在15872K的最后一个字节的位置。但这就出现一个问题，在不同的版本中，这个magic number的位置会是在不同的partition的最后一个字节。比如某个版本可能是在/dev/mtdblock/4的最后，但再后续的版本它会变成了/dev/mtdblock/7的最后面，这样就会存在很大的不确定性。所以在一个各个版本中，写magic number标记位时，需要一个统一的方法来做到这件事。最容易想到的办法当然就是magic number这个位置相对起始位置０是不变的。而前面提到过的/dev/mtdblock/0就刚好是代表了可以操作的整个flash分区。</p>
<p>有了/dev/mtdblock/0，这样我们就可以open 它，seek到magic number的位置，然后write下0x55或0xAA，这样就保持了写magic number的代码的一致性，不需要根据不同的分区，多次修改操作magic number的有关函数。</p>
<p><a name=N10208><span class=smalltitle>Booloader：</span></a></p>
<p>Bootloader的修改，也涉及到对magic number的读取，它的读取就相对简单一些，直接使用magic number在RAM中映射的绝对地址即可。</p>
<p>Bootloader检查完magic number后，需要将相对地址为0xBC0000的safe mode的kernel + rootfs读入到RAM，然后设置启动参数，调用内核，进入safe mode提示界面。</p>
<p><a name=N10214><span class=smalltitle>Linux kernel：</span></a></p>
<p>与老的、不带safe mode的image相比，新的image里的Linux kernel从总体的角度来说，并没有大的变化。在新做的master与safe mode的image中，它们各自需要包含一个Linux kernel，这两个kernel唯一的不同就是启动时所需要的rootfs在RAM中的映射位置不同。它们都有着相同的partition分区设置，编译选项等。</p>
<p>Safe mode必须包含自己的Linux kernel，因为它是运行在master损坏的情况下，master kernel已经不能启动了。</p>
<p><a name=N10220><span class=atitle>总结</span></a></p>
<p>上面的内容是在实际开发中对safe mode的设计与实现的一个描述。从这个描述中，可以看到safe mode在嵌入式Linux产品扮演着重要的角色，对它的设计涉及到很多方面，要考虑系统的尺寸，与现有buidling环境的的兼容性，对后续版本的升级的兼容性等诸多方面。</p>
<p>从某种意义上来说，safe mode的设计关系到产品的成败，一个好的safe mode的设计将会给产品带来巨大的灵活性与可扩展性，大大地方便了客户与产品开发商。 </p>
<span style="DISPLAY: none">DeQLinux联盟</span><br><span style="DISPLAY: none">DeQLinux联盟</span><br>
<p><a name=author><span class=atitle>关于作者</span></a></p>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td colSpan=3><img height=5 src="http://www.xxlinux.com/linux/d/file/article/development/embed/2007-09-06/3eecc96320710c62a1e5a720edb45f8e.gif" width=571></td>
        </tr>
        <tr vAlign=top align=left>
            <td>
            <p>&#160;</p>
            </td>
            <td><img height=5 src="http://www.xxlinux.com/linux/d/file/article/development/embed/2007-09-06/3eecc96320710c62a1e5a720edb45f8e.gif" width=4></td>
            <td width="100%">
            <p>余涛，高级软件工程师，现从事 linux 嵌入式系统的开发工作，主要研究方向嵌入系统，UPNP 多媒体播放系统。您可以通过电子邮件 <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#121;&#117;&#116;&#54;&#49;&#54;&#64;&#50;&#49;&#99;&#110;&#46;&#99;&#111;&#109;">yut616@21cn.com</a> 和他联系。希望能与更多的朋友交流关于 Linux 方面的知识。</p>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/39328.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2008-01-25 20:53 <a href="http://www.cnitblog.com/zouzheng/articles/39328.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>汉字的动态编码与显示方案</title><link>http://www.cnitblog.com/zouzheng/articles/39327.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Fri, 25 Jan 2008 12:45:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/39327.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/39327.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/39327.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/39327.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/39327.html</trackback:ping><description><![CDATA[&nbsp;<strong>摘要：</strong>综合几种常用单片机汉字显示方案，提出一种基于PC机预处理的汉字动态编码和动态字库的显示方法，较好地解决了存储空间、显示速度、软件开发维护几方面的相互矛盾；具有平台化的优点，同时，给出针对MCS51优化的汇编显示例程。
<p><span>&nbsp;&nbsp;&nbsp; <strong>关键词：</strong></span>机内码 动态编码 字库</p>
<p>因为汉字本身的特点，显示汉字始终是计算机在我国应用普及的一个障碍。最初，为了能在PC机上显示、处理汉字，国人发明了一种硬件设备"汉卡"，后来各种各样的采用纯软件技术的中文DOS逐渐成熟，其中、西文软件的运行速度和性能还是有明显的差距。最终在软件进入支持UNICODE、真正实现国际化的WIN95以后，硬件跨入"奔腾"时代，才实现了汉字与西文的统一显示，但是这一切是以硬件资源的飞速发展为前提的。以国际GB2312为例，一、二级汉字库共收录了6000多个汉字，每个字按16&#215;16点阵计算，字模需要占用32字节的存储空间，整个字库的规模在200k字节以上，高点阵（24点阵以上）和矢量字库以及Windows用的TrueType字体的字库规模都是几兆字节大小，这在早期的386时代是难以想象的。单片机因为使用灵活、结构简单、体积小、成本低而在工业和生活中得到广泛应用，也正是因此，它的硬件资源很有 限，寻址和计算机能力都远低于PC机，显示汉字更受限制。人们不满足单片机系统采用LED数码管的简单显示，根据单片机的特点，开发出了很多种汉字显示方法。</p>
<p><strong>1 几种常用单片机显示汉字方法</strong></p>
<p>(1)采用标准字[1]</p>
<p>这种方法仿器中文DOS的办法，将一个标准的汉字库装入ROM存储器，再根据汉字的机内码在字库中寻址，找到对应的字模，提取后送到显示器显示。因为采用了和PC机相同的编码(机内码)，软件的开发和维护非常简单，基本上与写PC机软件差不多。而对单片机系统自身的要求则相对高多了，16&#215;16点阵的字库需要256K字节，但是一般8位单片机的寻址能力只有64K字节，要进行存储器扩充，除增加很大一部分硬件成本外，还因为要进行存储器分页管理、地址切换，显示速度明显受影响，而且只能显示一种点阵字体。</p>
<p>（2）直接固化显示字模[2]</p>
<p>将要显示的语句中全部汉字的字模数据依次提取出来，顺序存放在存储器中，当显示时，直接取出字模数据送至显示器即可。这种方法占用空间少，程序实现简单，显示速度快；但是字模数据的提取和存储安排是一件委有繁琐的事件，要想大量显示汉字或进行程序修改几乎是不可能的，软件的可维护性很差。</p>
<p>(3)建立带索引的小字库[3]</p>
<p>将全部要显示的汉字统一建成一个小字库，字库分为2部分：索引素和字模表。索引表由若干定长记录组成，记录的内容为：汉字机内码、地址码、识别码。其中地址码是该汉字字模在字模表中的位置，识别码标志该汉字的点阵形式或字体等。字模表中按素引存放汉字字模。显示汉字时先根据待显汉字的机内码在索引表中寻找，找到对应索引记录后，读出地址码和识别码，再根据此从字模表中读出字模，送显即可。这种方法可根据实际使用对字库进行裁剪，硬件开销较小，但是要进行复杂的查询运算，字多了平均寻找时间就会变长，效率降低。</p>
<p><strong>2 汉字动态编码</strong></p>
<p>综上所述，我们发现：在方法1中，程序员工作量最少，但单片要机的软、硬件开销最大；方法2中，单片机的开销较少，但是编写和维护软件极为困难；方法3,介于二者之间。显然，存储空间、显示速度、软件开发维护件间存在着矛盾。受各种PC机模拟软件的启发，我们提出一种基于PC机预处理的汉字显示方法--汉字动态编码，在实际应用中较好地解决了这一问题。其基本原理如下：建立一种新的编码机制，这个汉字编码是动态的；一个编码不与某个汉字具体相联系，而仅代表某个汉字在字库中的位置（这个位置也是动态的）；用该码代替程序里字符串（C语言）或数据段（汇编语言）内汉字的机内码，单处机显示程序可根据这个新的编码直接在专门建立的动态小字库中找到字模，不用进行复杂的寻址、查找等运算，如图1所示。</p>
<p>实现汉字动态编码的过程就是先进行汉字识别，然后建立编码字典、提取字模、建立动态字库、改写机内码。首先扫描一遍程序文件，识别其中的汉字，将它们按出现先后顺序或机内码的大小排序，重复出现的剔除，建立了一个编码字典；根据汉字在编码字典的位置（序号），可以对汉字按区码、位码进行编码，也可以采用其它的方法编码，总之序号与它的动态编码存在一一对应关系；根据字典中每个汉字的机内码依次从PC机的汉字点阵字库中提取字模，顺序存储，建立一个小规模的动态字库，这样每个汉字的字模在字库中的位置就与其在编码字典中的序号、动态编码一一对应了。最后，再扫描一遍程序文件，按照编码字典将每个汉字的机内码改写为对应的动态编码。因为程序文件中的汉字随时会增减，编码随之而变，字库的大小也随时在变。所以称之为动态编码和动态字库。</p>
<p>考虑一般应用场合，1000个左右的汉字即可满足要求，按照汉字动态编码方法所需的字库仅为32K字节大小，只需要1片27256即可，几乎不用增加什么硬件。这样，字库的大小可由汉字的多少控制，程序的编写和维护可以沿用中文系统下的习惯，仅需要编写好的单片机程序用PC机进行一次预处理，程序员从繁杂的汉字处理工作中解放出来，有效地降低了软件和硬件开发成本。<br><img onclick=javascript:window.open(this.src); height=439 hspace=10 src="http://www2.minitos.com/article/UploadPic/2007-10/20071010141630290.gif" width=545 onload="return imgzoom(this,550);" vspace=10 border=0><br><strong>3 汉字动态编码的具体实现</strong></p>
<p>实现汉字动态编码的关键是建立编码字典和改写机内码。下面以是显示1行汉字"天上有个太阳，水中有个月亮"为例，说明动态编码的实现过程。</p>
<p>（1）汉字识别</p>
<p>汉字在PC机内的存储和处理是用机内码来实现的。每个汉字的机内码是唯一的，由2个字节组成，分区码和位码，为了和西文的ASCII码有区别，汉字机内码的区码和位码的取值都大于0A0H。我们要处理的源程序文件都是文本文件，存储的都是西文字符、控制符的ASCII码和中文字符的机内码，当扫描到文件中大于0A0H的字节内容时，即可判断该字节是汉字机内码的1个字节，而且肯定是成对出现，第1个字节是区别，第2个字节是位码，都大于0A0H，否则出错。</p>
<p>在C和汇编程序中表示字符的方式有所不同，但最终字符在文件中的存储格式是一样的。显示上面那行汉字，用C语言可以表示为：</p>
<p>char OneSent[]="天上有个太阳，水中有个月亮"；</p>
<p>printfhz（OneSent）;/*printfhz()显示函数*/</p>
<p>用十六进制编辑器（我们用的是UEdit32）察看文件中C语言字符串定义语句为：</p>
<p>63 68 61 72 20 20 4F 6E 65 53 65 6E 74 5B 5D 20 3D 20 22 CC EC C9 CF D3 D0 B8 F6 CC AB D1 F4 A3 AC CB AE D6 D0 D3 D0 B8 F6 D4 C2 C1 C1 22 20 3B 0D 0A</p>
<p>用汇编语言可以表示为：</p>
<p>ONESENT:DB '天上有个太阳，水中有个月亮'，00H</p>
<p>MOV DPTR,ONESENT</p>
<p>LCALL DISPLAY；DISPLAY是显示子程序</p>
<p>用十六进制编辑器察看上面用汇编语言定义字符串的那一条语句为：</p>
<p>4F 4E 45 53 45 4E 54 3A 44 42 20 27 CC EC C9 CF D3 D0 B8 F6 CC AB D1 F4 A3 AC CB AE D6 D0 D3 D0 B8 F6 D4 C2 C1 C1 27 2C 30 30 48 0D 0A</p>
<p>由此可以观察到情况确如前所述。</p>
<p>（2）建立编码字典</p>
<p>编码字典是在扫描的同时逐步建立起来的，每扫描到一个汉字（包括全角符号），即与字典中已有的字符进行比较，如没有重复，是新的字符就顺序存入字典，否则继续扫描，直至文件结属。由于每个字符都是从尾部添加的，它们的序号也是依次递增的，根据序号就可以进行动态编码了。由于显示的汉字一般都得在256个以上，即使进行动态编码，也需要用2字节编码来实现。以MCS51系列单片机和16&#215;16点阵汉字做一优化编码示例：8051的地址指针DPTR是16位指针，由高、低2字节指针DPH、DPL组合而成，如果将存储器按0FFH（256）字节分布，修改DPH即可直接寻址到任一页，修改DPL可寻址该页的任一字节。一个16&#215;16点阵汉字的字模是32字节大小，每页存储器正好能容纳8个汉字字模。可以优化设计动态编码的高字节指向字模的页地址（DPH），低字节指向字模在该页的首地址（DPL）。考虑地址空间的有效分配，将字库的地址放在0A000H以后（程序或数据存储器均可），动态编码的高字节要加上地址有效分配，将字库的地址放在0A000H以后（程序或数据存储器均可），动态编码的高字节要加上地址的页偏移量（大于等于0A0H）；考虑汉字与西文字符的区别，动态编码的低字节也需要加上一个大于或等于0A0H的偏移量。设某汉字在编码字典中的序号为Num，则该汉字的动态编码为：</p>
<p>动态编码高字节=页偏移量+Num/8</p>
<p>动态编码低字节=偏移量+(Num%8)&#215;32&nbsp;&nbsp;&nbsp; (1)</p>
<p>偏移量一般可设为0A0H。当单片机显示某个汉字时，只需将其动态编码的高字节送DPH，低字节减0A0H后送DPL，即可得到对应字模的地址指针。</p>
<p>（3）提取字模、建立动态字库</p>
<p>汉字机内码与点阵字库的详细关系可参考有关资料，它们存在如下联系：</p>
<p>字模首地址=（（机内码高字节-1）&#215;94+（机内码低字节-1））&#215;N&nbsp;&nbsp;&nbsp; （2）</p>
<p>注：N为一个汉字点阵字模的字节数。</p>
<p>按照编码字典内容，根据字模首地址，依次取出汉字字模，顺序写入一个二进制文件，即建成动态字库（其它方法略），用烧录器写入EPROM，就可以使用了。</p>
<p>（4）编码改写</p>
<p>机内码是PC机识别处理汉字用的，单片机只能处理我们建立起来的动态编码，还得把程序中汉字的仅机码根据编码字典改成对应的动态编码才行。由于在编写源程序的文本编辑器中看到的是经过系统处理过的字节，看不到汉字的机内码，也无法对其进行改写。根据"汉字识别"一节所述，不经过文本编辑器，直接将动态编码（十六进制数）定改磁盘文件对应位置即可，但是处理过后的汉字在文本编辑器里会显示出乱码。</p>
<p>（5）汉字显示</p>
<p>在明白了动态编码与动态字库中字模的关系后，可以完成按照PC机下汉字显示原理进行单片机下的程序设计，编写前面的函数printhz（）或子程序的DISPLAY，可参考相关资料[4]。<br><img onclick=javascript:window.open(this.src); height=131 hspace=10 src="http://www2.minitos.com/article/UploadPic/2007-10/20071010141631175.gif" width=545 onload="return imgzoom(this,550);" vspace=10 border=0><br><strong>4 MCS51汉字显示例程</strong></p>
<p>根据上述汉字动态编码方法，我们利用Borland C++编写了PC机预处理程序，将ASM51或C51源程序用PC机预处理后，建立了动态字库和改写了机内码，并且用ASM51写了一个针对MCS51进行优化的子程序DIS_CHAR。它显示一个西文或中文字符，实现过程如图2所示。</p>
<p>西文字符码的显示与流字显示基本相同，将西文字库（仅数字和字符部分）装入程序存储器中，根据ASCII码的值计算出字模首地址，将字符字模依次读出，再送显示即可。</p>
<p>此方案不但可用于单片机系统中，还可应用于任何无中文系统支持的嵌入式系统中。根据这个思路还可设计出不同字体、点阵混合的字库，支持包含2万多个字符的新国标编码，甚至矢量字体在单片机系统中的应用也成为可能。由于技术水平有限，此方案还存在一些不足之处，如改写编码后源程序中汉字显示为乱码，不知道改码处理是否正确，操作比较繁琐。如果能采用插件技术实现此方案，编辑器中能正常显示汉字，而输出已经是改码后的程序文件，则能很好地解决上述不足。在这里，我们抛码引玉，希望有兴趣的朋友一起合作，实现单片机中文显示的广义开发平台。</p>
<p>动态编码预处理的C语言源程序(在BC++3.1下调试通过)见网站补充版（http://www.dpj.com.cn）</p>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/39327.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2008-01-25 20:45 <a href="http://www.cnitblog.com/zouzheng/articles/39327.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Fedora 7 字体美化</title><link>http://www.cnitblog.com/zouzheng/articles/39238.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Wed, 23 Jan 2008 08:09:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/39238.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/39238.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/39238.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/39238.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/39238.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: edora7(Moonshine)发布也有一段时间了，这个版本包含了GNOME2.18和KDE3.5.6，美工有很大的提高；使用Xorg1.3版本，对nVidia显卡支持很好，支持显示器的热插拔和自动识别；yum性能也得到很大的提升，其它各方面的表现都很不错的，唯一的缺点就是中文字体显示不够完美。虽然比FC6有了极大的提高，但还是赶不上Ubuntu7.04。于是就有了自己动手美化中文显示的想法，下...&nbsp;&nbsp;<a href='http://www.cnitblog.com/zouzheng/articles/39238.html'>阅读全文</a><img src ="http://www.cnitblog.com/zouzheng/aggbug/39238.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2008-01-23 16:09 <a href="http://www.cnitblog.com/zouzheng/articles/39238.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>UCDOS汉字矢量字库(HZKSLxxJ)格式</title><link>http://www.cnitblog.com/zouzheng/articles/39235.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Wed, 23 Jan 2008 07:30:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/39235.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/39235.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/39235.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/39235.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/39235.html</trackback:ping><description><![CDATA[<span id="article"><font id="zoom">在UCDOS矢量字库中，每个汉字都是以128&nbsp;X&nbsp;128点阵制成矢量数据。
每个汉字的矢量数据都由一指针指向,&nbsp;指针区在每个汉字字库文件的开头0xBB3E字节。每个汉字矢量数据指针占6个字节,&nbsp;其格式为:前4个字节为汉字
的矢量数据在文件中的偏移,&nbsp;后2个字节为汉字的矢量数据的长度。</font>
<p><font id="zoom">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;汉字指针在指针区的偏移由公式计算:pos=((qu-16)*94+wei-1)*6。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;注:qu--区号。wei--位号。</font></p>
<p><font id="zoom">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;汉字的矢量数据格式为:控制码+坐标值。共有十种控制码,以下是控制码的&nbsp;含义:</font></p>
<p><font id="zoom">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(1)若控制码第7,6位为11,清除码,结束当前笔划,将第一个坐标与当前坐<br>标连线;建立新笔划,(X,Y)各占7位,由控制码的第５位开始,即:11XXXXXX&nbsp;XYYYYYYY。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;注:一个字节的位:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;7&nbsp;6&nbsp;5&nbsp;4&nbsp;3&nbsp;2&nbsp;1&nbsp;0<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;X&nbsp;X&nbsp;X&nbsp;X&nbsp;X&nbsp;X&nbsp;X&nbsp;X</font></p>
<p><font id="zoom">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(2)若控制码小于等于0x40,之后控制码大小个字节为坐标值,每个坐标占一个字节,共有控制码大小个坐标,(X,Y)坐标各占4位,其自的最高位为符号位,&nbsp;即:FXXXFYYY。</font></p>
<p><font id="zoom">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(3)若控制码的高4位等于4,之后控制码的低4位大小个字节为坐标值,每个坐标占一个字节,共有控制码大小个坐标,(X,Y)坐标各占4位,X为正,Y为正,即:+XXXX+YYYY。</font></p>
<p><font id="zoom">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(4)若控制码的高4位等于5,之后控制码的低4位大小个字节为坐标值,每个坐标占一个字节,共有控制码大小个坐标,(X,Y)坐标各占4位,X为负,Y为正,即:-XXXX+YYYY。</font></p>
<p><font id="zoom">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(5)若控制码的高4位等于6,之后控制码的低4位大小个字节为坐标值,每个坐标占一个字节,共有控制码大小个坐标,(X,Y)坐标各占4位,X为负,Y为负,即:-XXXX-YYYY。</font></p>
<p><font id="zoom">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(6)若控制码的高4位等于7,之后控制码的低4位大小个字节为坐标值,每个坐标占一个字节,共有控制码大小个坐标,(X,Y)坐标各占4位,X为正,Y为负,即:+XXXX-YYYY。</font></p>
<p><font id="zoom">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(7)若控制码等于0x80,&nbsp;其后1字节为Y坐标值,&nbsp;最高位为符号位,&nbsp;X坐标不&nbsp;变,&nbsp;即:10000000&nbsp;FYYYYYYY。</font></p>
<p><font id="zoom">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(8)&nbsp;若控制码等于0x90,&nbsp;其后1字节为X坐标值,&nbsp;最高位为符号位,&nbsp;Y坐标不&nbsp;变,即:10000001&nbsp;FXXXXXXX。</font></p>
<p><font id="zoom">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(9)&nbsp;若控制码的高4位等于8,其后1字节为Y坐标值,控制码的低4位值为X坐标值,X坐标各占4位,最高位为符号位,Y坐标各占8位,最高位为符号位,&nbsp;即:1000FXXXFYYYYYYY。</font></p>
<p><font id="zoom">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(10)若控制码的高4位等于9,其后1字节为X坐标值,控制码的低4位值为Y坐标值,Y坐标各占4位,最高位为符号位,X坐标各占8位,最高位为符号位,&nbsp;即:1000FYYYFXXXXXXX。</font></p>
<p><font color="#ffffff">主题：Re: 请问ucdos矢量字库算法</font></p>
<p><font color="#408080">:&nbsp;&nbsp;&nbsp;&nbsp;我想直接读取ucdos下的矢量字库，请问有谁知道算法？</font><br><font color="#408080">:&nbsp;&nbsp;&nbsp;&nbsp;急！急！急?/font&gt;<br><font color="#408080">:&nbsp;?/font&gt;<br><br><br>/*&nbsp;矢量汉字的读取和显示&nbsp;*/<br>#include&nbsp;&lt;stdio.h&gt;<br>#include&nbsp;&lt;graphics.h&gt;<br>#define&nbsp;HZNUM&nbsp;(0xf7-0xaf)*(0xfe-0xa0)&nbsp;/*&nbsp;除前16区外的所有汉字&nbsp;*/<br>#define&nbsp;HZKSIZE&nbsp;128&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;汉字的大小&nbsp;*/<br>#define&nbsp;START_X&nbsp;0<br>#define&nbsp;START_Y&nbsp;0<br>#define&nbsp;VIEW_H&nbsp;256&nbsp;&nbsp;&nbsp;/*&nbsp;显示汉字的高度&nbsp;*/<br>#define&nbsp;VIEW_W&nbsp;256&nbsp;&nbsp;&nbsp;/*&nbsp;显示汉字的宽度&nbsp;*/<br>FILE&nbsp;*fp;<br>struct&nbsp;hz_struct&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;long&nbsp;shift;&nbsp;&nbsp;/*&nbsp;偏移量&nbsp;*/<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;int&nbsp;size;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;大小&nbsp;*/<br>&nbsp;}HZ_Index[HZNUM];&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;汉字索引&nbsp;*/<br>unsigned&nbsp;char&nbsp;buf[1024];<br>unsigned&nbsp;char&nbsp;dotbuf[1024];<br>main()<br>{<br>&nbsp;&nbsp;long&nbsp;ioffset;<br>&nbsp;&nbsp;int&nbsp;i,j;<br>&nbsp;&nbsp;char&nbsp;ch;<br>&nbsp;&nbsp;int&nbsp;gdriver=DETECT;<br>&nbsp;&nbsp;int&nbsp;gmode;<br>&nbsp;&nbsp;if((fp=fopen("hzksly1j","rb"))==NULL){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("cano't&nbsp;open&nbsp;the&nbsp;HZlib!");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exit(1);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;fread(HZ_Index,sizeof(struct&nbsp;hz_struct),HZNUM,fp);<br>//if(registerbgidriver(EGAVGA_driver)&lt;0) exit(1);<br>  initgraph(&amp;gdriver,&amp;gmode,"");<br>  cleardevice();<br>  setcolor(LIGHTGRAY);<br>  for(i=0; i&lt;HZNUM;i++){<br>         Disp_HZ(HZ_Index[i].size,HZ_Index[i].shift);<br>         rectangle(START_X,START_Y,START_X+256,START_Y+256);<br>         ch=getch();<br>         cleardevice();<br>         if(ch=='q') break;<br>  }<br>  closegraph();<br>  fclose(fp);<br>}<br><br>/* 显示汉字 */<br>Disp_HZ(int length,long posi)<br>{<br>  int i,j,k;<br>  int x0,y0,x1,y1;<br>  int hzsize;<br>  union utype{<br>         unsigned short size;<br>         unsigned char str[2];<br>  }BH;<br>  if((fseek(fp,posi,0))!=0){<br>         printf("seek\"clib\"error!\n");<br>         exit(0);<br>  }<br>  memset(buf,0,1024);<br>  fread(buf,length,1,fp);<br>  hzsize=decode(buf,length);<br>  k=0;<br>  for(i=0;i&lt;hzsize;i++){<br>         BH.str[0]=dotbuf[k++];<br>         BH.str[1]=dotbuf[k++];<br>         if(BH.size==0)break;/* 每个汉字以0结束 */<br>         x0=START_X+(dotbuf[k++]*VIEW_W)/HZKSIZE;<br>         y0=START_Y+(dotbuf[k++]*VIEW_H)/HZKSIZE;<br>//       x0=START_X+dotbuf[k++];<br>//       y0=START_Y+dotbuf[k++];<br>         moveto(x0,y0);<br>         for(j=0;j&lt;BH.size-1;j++){<br>                 x1=START_X+(dotbuf[k++]*VIEW_W)/HZKSIZE;<br>                 y1=START_Y+(dotbuf[k++]*VIEW_W)/HZKSIZE;<br>//       x1=START_X+dotbuf[k++];<br>//       y1=START_Y+dotbuf[k++];<br>                 lineto(x1,y1);<br>                }<br>                lineto(x0,y0);<br>  }<br>}<br><br>/* 汉字字形还原<br> */<br>decode(p,length)<br>unsigned char *p;<br>int length;<br>{<br>  int k;<br>  int i,count,lposi,xsum,ysum;<br>  unsigned char b60;<br>  char dxfh,dyfh;<br>  char x0,dx,dy;<br>  lposi=0;<br>  k=2;<br>  while((p-buf)&lt;=length){<br>  b60=*p&amp;0xc0;<br>  switch(b60){<br>  case 0xc0:<br>            if(k!=2){<br>              dotbuf[lposi]=(k-lposi-2)/2;<br>              dotbuf[lposi+1]=0;<br>              lposi=k++;<br>              k++;<br>            }<br>            x0=(*p&amp;0x3f)&lt;&lt;1;<br>            dx=(*(p+1)&gt;&gt;7)&amp;0x01;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dx=dx+x0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p++;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dy=*p++&amp;0x7f;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dotbuf[k++]=xsum=dx;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dotbuf[k++]=ysum=dy;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;<br>&nbsp;&nbsp;case&nbsp;0x80:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dxfh=dyfh=1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;switch(*p&amp;0x30){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case&nbsp;0x00:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(*p&amp;0x08)dxfh=-1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dx=*p&amp;0x07;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p++;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(*p&amp;0x80)dyfh=-1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dy=*p&amp;0x7f;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case&nbsp;0x10:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(*p&amp;0x08)dyfh=-1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dy=*p&amp;0x07;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p++;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(*p&amp;0x80)dxfh=-1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dx=*p&amp;0x7f;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case&nbsp;0x20:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case&nbsp;0x30:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p++;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(*p&amp;0x80)dxfh=-1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dx=*p&amp;0x7f;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p++;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(*p&amp;0x80)dyfh=-1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dy=*p&amp;0x7f;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p++;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xsum+=dx*dxfh;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ysum+=dy*dyfh;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dotbuf[k++]=xsum;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dotbuf[k++]=ysum;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;<br>&nbsp;&nbsp;case&nbsp;0x40:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dxfh=*p&amp;0x30;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(dxfh==0){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dxfh=1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dyfh=1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else&nbsp;if(dxfh==0x10){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dxfh=-1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dyfh=1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else&nbsp;if(dxfh==0x20){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dxfh=-1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dyfh=-1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else&nbsp;if(dxfh==0x30){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dxfh=1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dyfh=-1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;count=*p++&amp;0x0f;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(i=0;i&lt;count;i++){<br>              dx=*p&gt;&gt;4;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dy=*p&amp;0x0f;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xsum+=dxfh*dx;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ysum+=dyfh*dy;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dotbuf[k++]=xsum;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dotbuf[k++]=ysum;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p++;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;<br>&nbsp;&nbsp;case&nbsp;00:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;count=*p++&amp;0x3f;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(i=0;i&lt;count;i++){<br>             if(*p&amp;0x80) dxfh=-1;<br>             else dxfh=1;<br>             if(*p&amp;0x08)dyfh=-1;<br>             else dyfh=1;<br>             dx=(*p&amp;0x70)&gt;&gt;4;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dy=(*p&amp;0x07);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xsum+=dx*dxfh;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ysum+=dy*dyfh;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dotbuf[k++]=xsum;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dotbuf[k++]=ysum;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p++;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;dotbuf[k++]=0;<br>&nbsp;&nbsp;&nbsp;dotbuf[k++]=0;<br>&nbsp;&nbsp;&nbsp;dotbuf[lposi]=(k-lposi-2-2)/2;<br>&nbsp;&nbsp;&nbsp;dotbuf[lposi+1]=0;<br>&nbsp;&nbsp;&nbsp;return&nbsp;k;<br>&nbsp;}</font></font></p>
</span><img src ="http://www.cnitblog.com/zouzheng/aggbug/39235.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2008-01-23 15:30 <a href="http://www.cnitblog.com/zouzheng/articles/39235.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> MiniGUI 1.3.3 移植详解</title><link>http://www.cnitblog.com/zouzheng/articles/39234.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Wed, 23 Jan 2008 07:28:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/39234.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/39234.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/39234.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/39234.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/39234.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 本文是用 Word 写的，本来想当成附件发上来，结果系统说&#8220;发贴数大于10篇才能上传附件！&#8221;，让我暴汗～～～～～ 所以就直接 Copy 上来了，有Word 中有一些加红，加黑表示要注意的地方这里显示不出来，大家将就着看吧，呵呵，有什么错误希望大家指出，谢谢  MiniGUI 1.3.3 移植详解  作者：大漠孤狼 &nbsp; E-Mail：yuqiang0107@126....&nbsp;&nbsp;<a href='http://www.cnitblog.com/zouzheng/articles/39234.html'>阅读全文</a><img src ="http://www.cnitblog.com/zouzheng/aggbug/39234.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2008-01-23 15:28 <a href="http://www.cnitblog.com/zouzheng/articles/39234.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>U-Boot在S3C2410上的移植分析</title><link>http://www.cnitblog.com/zouzheng/articles/36887.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Mon, 26 Nov 2007 13:13:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/36887.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/36887.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/36887.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/36887.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/36887.html</trackback:ping><description><![CDATA[<div style="FLOAT: right"><a href="http://www.edires.net/3700/order.aspx" target=_blank><img height=250 src="http://www.edires.net/3700/Series_37001.gif" width=300 border=0></a>&nbsp;</div>
<span class=n12 id=lblContent><strong>摘　要</strong>：介绍了一款优秀的嵌入式<a class=keyword href="http://www.edires.net/snewssystem/search.aspx?q=BootLoader" target=_blank><u><font color=#000000>BootLoader</font></u></a>—<a class=keyword href="http://www.edires.net/snewssystem/search.aspx?q=U-Boot" target=_blank><u><font color=#000000>U-Boot</font></u></a>，详细讲解了它的运行原理，着重讨论了其在<a class=keyword href="http://www.edires.net/snewssystem/search.aspx?q=S3C2410" target=_blank><u><font color=#000000>S3C2410</font></u></a>上的<a class=keyword href="http://www.edires.net/snewssystem/search.aspx?q=移植" target=_blank><u><font color=#000000>移植</font></u></a>过程，并对移植结果进行了测试。 <br><br><strong>关键词</strong>：U-Boot；S3C2410；移植；BootLoader&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>引　言</strong>&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;BootLoader是<a class=keyword href="http://www.edires.net/snewssystem/search.aspx?q=嵌入式系统" target=_blank><u><font color=#000000>嵌入式系统</font></u></a>软件开发的第一个环节，它紧密地将软硬件衔接在一起，对于一个嵌入式设备后续的软件开发至关重要。BootLoader还涉及到许多硬件相关的知识，对于普通的嵌入式开发板，它又是不可跳过的步骤，所以做好它的移植工作是必须的，对于后续的开发工作也是有益的。U-Boot是当前比较流行、功能强大的BootLoader，它操作简便，可以支持多种体系结构的处理器，同时提供了完备的命令体系。 S3C2410是三星公司一款基于<a class=keyword href="http://www.edires.net/snewssystem/search.aspx?q=ARM" target=_blank><u><font color=#000000>ARM</font></u></a>920T的嵌入式通用处理器。本文的移植平台就是以S3C2410 为核心的HHARM9-EDU-R2开发板，这块开发板的硬件资源配置较为完善。主要硬件资源有：S3C2410处理器；1片Intel TE28f128FLASH ( 16M)；2片Hynix HY57V561620 SDRAM(64M)；10/100M自适应网络芯片DM9000； USBHost/Device；RS232&#215;2/RS485&#215;1串口；LQ035FLM08L 256K色TFT真彩LCD显示屏；全功能JTAG调试口等。&nbsp;<br><br><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;U-Boot简介</strong>&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;U-Boot是Das U-Boot的简称，是由denx软件中心依照GPL 发布的公共软件，作为系统启动的引导模块，U-Boot支持多种处理器架构，比如Power-PC、ARM、MIPS和x86等。 目前，U-Boot源代码在sourceforge网站的社区服务器中， Internet上有一群自由开发人员对其进行维护和开发，它的项目主页是http：//sourceforge.net/ projects/ u-boot。当下载并解压U-Boot 源码包后，会形成如下的目录结构：board，和一些已有开发板有关的文件；common，实现各种U-Boot 命令的C 文件；cpu，CPU相关文件，其中的子目录都是以U-Boot 所支持的CPU命名的；disk，disk驱动的分区处理代码；doc，文档；drivers，通用设备驱动程序；fs，支持文件系统的文件；include，头文件，对各种硬件平台支持的汇编文件，系统的配置文件和对文件系统支持的文件；net：与网络有关的代码； lib-arm，与ARM体系结构相关的代码；tools，创建S-Record 格式文件和U-Boot Images的工具。&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;本文中U-Boot的移植就是根据HHARM9-EDU-R2开发板的硬件资源在以上的目录中修改或者添加相关源文件，并且重新编译的过程。移植工作开始之前，了解U-Boot的运行过程是十分必要的。&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>U- Boot 运行过程分析</strong>&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;U-Boot编译后的代码定义一般不超过100kB，并且这100 kB又分成两个阶段来执行。第一阶段的代码在start.s中定义，大小不超过10 kB，它包括从系统上电后在0x00000000 地址开始执行的部分。这部分代码运行在Flash中，它包括对S3C2410的一些寄存器的初始化和将U-Boot的第二阶段代码从Flash拷贝到SDRAM中。除去第一阶段的代码，剩下的部分都是第二阶段的代码。 第二阶段的起始地址是在第一阶段代码中指定的，被复制到SDRAM后，就从第一阶段跳到这个入口地址开始执行剩余部分代码。 第二阶段主要是进行一些BSS 段设置，堆栈的初始化等工作，最后会跳转到main-loop函数中，接受命令并进行命令处理。图1 给出了U-Boot的详细的运行过程包括对内核的设置、装载及调用过程。&nbsp;<br><img src="http://www.edires.net/sNewsSystem/Files/Images/3464.jpg" border=0><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;图1　U-Boot运行过程<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;了解了U-Boot 的运行过程以后，我们还必须确定开发板的地址空间分布，才可以进行源码的修改和移植工作。 地址空间的分布部分依赖于开发板的硬件配置及CPU 的复位地址。 本文中开发板的地址空间如图2 所示。&nbsp;<br><img src="http://www.edires.net/sNewsSystem/Files/Images/3465.jpg" border=0><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;图2 &nbsp;开发板地址空间分布&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>U-Boot的移植与测试</strong>&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;为了使移植工作更加快捷，应当选择U-Boot当前发布的最新版本1.1.2 (尽管通过CVS可以得到U-Boot1.1.3，但其正在开发，尚未发布，不宜使用) 。 因为最新的版本可以提供尽可能多的处理器核及开发板的支持。 对于U-Boot-1.1.2 而言，它不仅提供对ARM- 920T内核的支持，而且直接提供了对于S3C2410 的板级支持，这使移植工作量相对减少。&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>支持ARM- 920T内核的代码修改</strong>&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;由于U-Boot-1.1.2 提供对ARM-920T 内核的直接支持，所以本步骤不需要做任何工作，本文为了让读者了解BootLoder 移植的通用模式，在此只是稍加提示。&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>配置自己的开发板</strong>&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>&nbsp;&nbsp;建立自己开发板的目录和相关文件</strong>。&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1) 在include/ configs目录中以smdk2410.h为模板添加头文件S3C2410. h(cp smdk2410.h S3C2410.h) 。 这个文件是开发板的配置文件，它包括开发板的CPU、系统时钟、RAM、Flash系统及其它相关的配置信息。&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2) 在board/目录下创建S3C2410目录。拷贝smdk2410目录下所有文件到S3C2410目录下，共有如下六个文件：flash.c、memsetup.c、S3C2410.c、Makefile、U-Boot .lds 和config.mk，根据开发板实际情况对各个文件进行修改。&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;◆flash.c.U-Boot读、写和删除Flash 设备的源代码文件。由于不同开发板中Flash 存储器的种类各不相同，所以，修改flash.c时需参考相应的Flash 芯片手册。它包括如下几个函数：&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unsigned long flash-init (void )，Flash初始化； <br>　　int flash-erase (flash-info-t *info，ints-first，ints -last)，Flash擦除； <br>　　volatile static int write- hword (flash-info-t *info，ulong&nbsp;dest ， ulong data) ，Flash 写入； <br>　　int write-buff (flash-info-t *info，uchar *src ，ulong&nbsp;addr，ulong cnt)，从内存复制数据。&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;由于本文开发板所用flash芯片为IntelTE28f128，在board/ cmi目录中有此flash.c，只需对其稍加修改即可使用。&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;◆memsetup.c.初始化时钟、SMC控制器和SDRAM控制器。为了以后能用U-Boot的GO命令执行修改过的用loadb或tftp下载的U-Boot.在标记符&#8220;0：&#8221;上加入五句： <br>　　&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov r3，pc <br>　　ldr r4，= 0x3FFF0000 <br>　　and r3，r3，r4 / /以上三句得到实际起动的内存地址 <br>　　aad r0，r0，r3 / /用GO 命令调试uboot时，启动地址在RAM <br>　　add r2，r2，r3 / /把初始化内存信息的地址，加上实际起动地址&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;◆S3C2410.C.设置各种总线时钟，打开数据Cache和指令Cache，并设置相关内存参数。&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;◆Makefile.修改：OBJS ：= S3C2410.o flash.omemsetup.o&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;◆U-Boot.lds.作如下修改：&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.text&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cpu/ arm920t/ start.o ( .text)&nbsp;<br>* (.text)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;◆config.mk.用于设置程序连接的起始地址，因为会在U-Boot 中增加功能，所以留下6M 的空间，修改33F80000 为33A00000。&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>实现网卡的驱动程序</strong>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在drivers/目录中以dm9000x.c和dm9000x.h为模板添加网口设备控制程序dm9000.c和dm9000.h，其中dm9000.c主要包括以下函数：&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int eth-init (bd-t *bd)，初始化网络设备；&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int eth-send(volatile void *，int)，发送数据包；&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int eth-rx(void)，接收数据包。&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void eth-halt (void)，关闭网络设备；&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;为了方便网络设备的数据读写操作，还定义了如下函数：&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static int dm9000-probe (void)，搜索DM9000芯片，分配空间并登记之；&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static u16 phy-read(int)，从Phyxcer寄存器读取一个字；&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static void phy-write ( int，u16)，写一个字到Phyxcer 寄存器；&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static u16 read-srom-word (int)，从SROM 读取一个字数据；&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static u8 DM9000-ior (int)，从I/ O 口读取一个字节；&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static void DM9000-iow(int reg，u8 value)，写一个字节到I/ O 口；&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;最后在drivers/Makefile中加入dm9000.o。&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;修改Makefile 文件&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在U-Boot-1.1.2/Makefile中ARM92xT Systems注释下面加入以下两行：&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;S3C2410-config ：unconfig <br>　　@./ mkconfig ＄( @：-config = ) arm arm920tS3C2410&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;其中&#8220;arm&#8221;是CPU的种类，arm920t是ARM CPU对应的代码目录，S3C2410是自己开发板对应的目录。&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;交叉编译器安装在：/ path/ armv4l-unknown -linux-目录下，所以把CROSS-COMPILE 设置成相应的路径： CROSS-COMPILE = / path/ arm4l-unknown-linux -&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;生成目标文件并进行测试&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;依次运行以下命令：&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# make clean&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# make S3C2410-config&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# make&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;之后会生成三个文件：&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;U-Boot ——ELF 格式的文件， 可以被大多数Debug 程序识别；&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;U-Boot.bin ——二进制文件，纯粹的U-Boot&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;二进制执行代码，不保存ELF 格式和调试信息。 这个文件一般用于烧录到用户开发板中；U-Boot .srec ——Motorola S-Record格式，可以通过串行口下载到开发板中。&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>测试与应用</strong>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1) 测试&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;利用编制好的Flash烧写程序，通过JTAG口将生成的二进制文件U-Boot。bin烧入Flash的零地址。 烧录成功后，拔掉JTAG调试线并复位开发板，从Minicom终端输出如下信息：&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;U-Boot 1.1.2 (Jul 20 2005-09 ：34 ：21)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;U-Boot code ： 33F00000-&gt; 33F1952C BSS：-&gt; 33F1D870&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RAM Configuration ：&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Bank # 0 ： 30000000 64 MB&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Flash Memory Start 0x0000000&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Device ID of the Flash is 18&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Flash ： 16 MB&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Write 18 to Watchdog and it is 18 now&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;In ： serial&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Out ： serial&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Err ： serial&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SMDK2410 #&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;串口输出的以上信息表明，CPU和串口已正常工作。通过U-Boot提供的命令flinfo和mtest可以测试Flash和RAM。经过测试，可以正确地读出Flash信息及读写RAM，表明Flash 和DRAM 已正确初始化。 用tftp命令传输宿主机tftpboot目录下任一小文件到RAM成功，说明网卡芯片也成功驱动。&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2) 简单应用&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;U-Boot的主要作用是用来引导内核。因此，通过U-Boot引导一个特定的内核，可以进一步测试其移植的稳定性。 而使用U-Boot引导内核有两种不同的方法。第一种方法是直接将内核映象文件和根文件系统烧写入Flash，使用此方法，U-Boot在启动时将Flash中的内核映象及根文件系统读入RAM指定位置并从同一位置启动内核。 第二种方法是将内核映象文件和根文件系统下载至RAM中直接启动(而不是从Flash中读入RAM) ，此种方法不需要烧写Flash。笔者为了减少烧写Flash的次数，在本文中采用第二种方法，其步骤如下：&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SMDK2410 # tftp 30008000 zImage&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SMDK2410 # tftp 30800000 ramdisk.Image.gz&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SMDK2410 # go 30008000&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;上述指令执行的过程中，未出现异常，内核成功启动，并最终进入Shell提示符&#8220; # &#8221;。在Shell提示符下输入内核编译时定制的各个命令，均可以正常运行。另外编写简单C程序，并用交叉编译器编译之，最终生成的可执行文件能够在开发板上正常运行。上述事实说明内核经过U-Boot引导已稳定运行在开发板上。此次应用，进一步验证了U-Boot移植的稳定性。至此，移植工作告一段落。&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>结　语</strong>&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;目前，笔者移植的U-Boot已经能稳定地运行在开发板上，这使得Linux内核的调试脱离了BDM调试器，节约了大量的开发时间，大大提高了效率，是对后续嵌入式开发的有力支持。 当然，U-Boot只是一款好用的BootLoader，嵌入式Linux的开发存在很多技术细节，只有根据实际情况不断修改、调试、总结，才能获得更大的成功。<br><br>
<p>4 移植前的准备</p>
<div>（1）、首先读读uboot自带的readme文件，了解了一个大概。</div>
<div></div>
<div>（2）、看看common.h，这个文件定义了一些基本的东西，并包含了一些必要的头文件。再看看flash.h，这个文件里面定义了 flash_info_t为一个struct。包含了flash的一些属性定义。并且定义了所有的flash的属性，其中，AMD的有： AMD_ID_LV320B，定义为&#8220;#define AMD_ID_LV320B 0x22F922F9&#8221;。</div>
<div></div>
<div>（3）、对于&#8220;./borad/at91rm9200dk/flash.c&#8221;的修改，有以下的方面：<br>&#8220;void flash_identification(flash_info_t *info)&#8221;这个函数的目的是确认flash的型号。注意的是，这个函数里面有一些宏定义，直接读写了flash。并获得ID号。</div>
<div></div>
<div>（4）、修改：&#8221;./board/at91rm9200dk/config.mk&#8221;为<br>TEXT_BASE=0x21f80000 为TEXT_BASE=0x21f00000 （当然，你应该根据自己的板子来修改，和一级boot的定义的一致即可）。</div>
<div></div>
<div>（5）、再修改&#8221;./include/configs/at91rm9200dk.h&#8221;为<br>修改flash和SDRAM的大小。</div>
<div></div>
<div>（6）、另外一个要修改的文件是：<br>./borad/at91rm9200dk/flash.c。这个文件修改的部分比较的多。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a． 首先是OrgDef的定义，加上目前的flash。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; b． 接下来，修改&#8221;#define FLASH_BANK_SIZE 0x200000&#8221;为自己flash的&nbsp; 容量<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; c． 在修改函数flash_identification(flash_info_t * info)里面的打印信息，这部分将在u-boot启动的时候显示。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; d． 然后修改函数flash_init(void)里面对一些变量的赋值。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e． 最后修改的是函数flash_print_info(flash_info_t * info)里面实际打印的函数信息。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; f． 还有一个函数需要修改，就是：&#8220;flash_erase&#8221;，这个函数要检测先前知道的flash类型是否匹配，否则，直接就返回了。把这里给注释掉。</div>
<div></div>
<div><br>（7）、接下来看看SDRAM的修改。<br>这个里面对于&#8220;SIZE&#8221;的定义都是基于字节计算的。<br>只要修改&#8221;./include/configs/at91rm9200dk.h&#8221;里面的<br>&#8220;#define PHYS_SDRAM_SIZE 0X200000&#8221;就可以了。注意，SIZE是以字节为单位的。</div>
<div></div>
<div>（8）、还有一个地方要注意<br>就是按照目前的设定，一级boot把u_boot加载到了SDRAM的空间为：21F00000 -&gt; 21F16B10，这恰好是SDRAM的高端部分。另外，BSS为21F1AE34。</div>
<div></div>
<div><br>（9）、编译后，可以写入flash了。<br>&nbsp;&nbsp;&nbsp;&nbsp; a． 压缩这个u-boot.bin<br>&#8220;gzip &#8211;c u-boot.bin &gt; u-boot.gz&#8221;<br>压缩后的文件大小为：<br>43Kbytes<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; b． 接着把boot.bin和u-boot.gz烧到flash里面去。<br>Boot.bin大约11kBytes，在flash的0x1000 0000 ~ 0x1000 3fff <br><br>在at91rm9200上移植u-boot<br><br>u-boot移植心得<br><font color=#555555>几经艰辛，终于基本完成了u-boot在s3c44b0的移植工作，在些记录一下在移植过程中所碰到的困难和解决方法（一些心得），作为日后参考之用，也希望能够帮到其它有需要的人^_^。<br>1.来由：<br>在我搞完ucos后(本来我是想学uclinux的，不过在对系统一无所知的情况下，还是先学一下ucos比较实际^_^，从中也可以对系统有个清晰的概念)，我意识到要搞系统的话一个功能强大的bootloader是必不可少的，而我的板上自带的是armboot（其实我个人觉得应用在ARM上的话armboot已经足够了，毕竟u-boot也是从armboot中发展过来的，纯属个人意见），如果我的板上自带光盘有已经移植好的armboot源代码供我参考的话，我想我也不会花这么多时间去搞u-boot，可恨的是我的光盘上只有armboot.bin这个二进制文件，而没有源文件，没办法，我下定主意自己搞一个，考虑了一番后我选择了u-boot，毕竟参考资料相对比较多，再我学完u-boot后再回头看armboot，简直是一个炉里出的饼，这是后话。<br>&nbsp; &nbsp; 2.准备：<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 说是容易，做起来却挺难。因为编译u-boot要在linux环境下，而不能在我们平时所熟悉的ads下那么直观。首先要建立好交叉编译环境，这个交叉编译环境可以自己来做，不过完全没必要，而且难度也挺大，一般是下载人家编译好的工具。我刚开始在这里就郁闷了很久，现在会了以后觉得原来就是这么简单，在些我把方法说清楚，希望不会再有人为这个问题郁闷了^_^：<br>1）在网上下载一个u-boot源代码，我用的是1.1.2版本的，最新的应该是1.1.4的吧，其实差不多，那就像我那样下载一个1.1.2版本的吧。把源文件解压，这个应该不用说了吧，学过linux的人应该会，不会的话我想你继续做下去也困难，那就先装个linux用下吧（我用的是RedHat的，哦对了，编译程序是需要gcc编译器的，所以安装方式一定要选择工作站哦^_^）。好了，解压后你发现在u-boot.1.1.2目录下有Makefile这个文件吧？让我们看看它里面的内容，最简单的方法就是vi Makefile了。我们要看的是它选择的是哪一个交叉编译器。可以看到这一项：<br>ifeq ($(ARCH),arm)<br>CROSS_COMPILE = arm-linux-<br>看到吧，也就是说这里所用的交叉编译器是arm-linux-gcc了，（u-boot默认是用这个的，也有用arm-elf-gcc的，网上有个工具包arm-elf-tools-20030314.sh,我试过用它来编译，没有问题，顺便提一下，arm-elf-gcc是用来编译uClinux内核的工具来的）那你就下载一个arm-linux-的交叉编译器吧，我是在网上下载cross-2.95.3.tar.bz2这个文件，然后解压得到2.95.3版本的交叉编译工具的，具体设置如下（参考网上资料）： </font>
<p>2）在宿主机上建立arm-linux-gcc交叉编译环境<br>在RedHat Linux系统下以root用户登录，<br>将cross-2.95.3.tar.bz2文件复制到/目录下，<br>安装：<br># tar jxvf cross-2.95.3.tar.bz2<br>这个命令会在你的/usr/local/arm/2.95.3目录下安装 arm-linux-gcc 交叉编译程序，<br>然后在PATH变量中添加一项：/usr/local/arm/2.95.3/bin.<br>[root@localhost root]# export ATH=/usr/local/arm/2.95.3/bin:$PATH<br>或<br>把PATH=/usr/local/arm/2.95.3/bin:$PATH添加到/ETC/bash_profile文件中<br>或者<br>在/etc/bashrc文件中添加一项:<br>export PATH=/usr/local/arm/2.95.3/bin:$PATH<br>测试：<br>把终端关闭，重新打开后执行如下命令：<br># arm-linux-gcc &#8211;v<br>好了，建立好交叉编译环境后可以试着编译u-boot了
<p>这里提几个注意点：<br>1.不可用winRAR解压u-boot-1.1.2.tar.bz2或u-boot-1.1.2.tar.gz这种文件（就个可能新手会犯，一般熟悉linux命令的人应该都不会这样做吧，在此还是要提一下）<br>2.可能下载的文件有一些中间文件会阻碍编译的运行，所以在编译前最好来个彻底清除，在u-boot.1.1.2目录下运行命令：make distclean （其实这个命令在Makefile文件下就有）我当时为这个问题郁闷了很久，希望你们不会像我这样^_^）;<br>3，有些人为了方便想在cygwin下编译，但是经常在网上看到在这个虚拟平台下编译有很多的问题，要配置的东西也多，而且好像我用过那个vi没有linux环境下的好用，所以最好还是不要用这个软件了吧，如果你真的离不开windows的话可以像我这样装个虚拟机，在虚拟机下再装linux的系统，具体参考这个网站）：http://fedora.linuxsir.org/doc/vmware/<br>3）好了，现在开始测试你的交叉编译器搞好没有。在u-boot.1.1.2目录下执行如下命令：<br>1)Make distclean (再次强调)<br>2)Make B2_config(随便再个现成的试试^_^)<br>3)Make (没错的话应该会生成u-boot.bin文件，发生错误的话也不怕，只要细心看一下哪里错就行了，gcc碰到错误后会退出编译，所以可以一个个错误来改，一般的问题都是没找到编译器（可能你没装或者装的不对，例如人家用的是arm-linux-而你装的是arm-elf-，如果你装了的话看看你的环境变量设好了没有，前面有讲，如果不关编译器的事的话那就再看看，一般是文件的后缀不对，有些文件后缀是大写的，例如start.S但是如果你的是start.s小写的话那当然找不到（解决方法很，把它改成大写就行了）。细心看吧，不用怕，它都有注明路径，很容易可以找到的）)<br>如果以上步骤都无误的话那么恭喜你，你的交叉编译环境可以用来编译你的u-boot源代码了，可以开始以下阶段。<br>3.移植：<br>说时迟那时快，现在开始移植工作(以下是我一步步重新做一遍，力求说得详细点，感谢我吧^_^)<br>我以B2板子的程序做为模板来做. <br>#cd u-boot-1.1.2<br>#cd board <br>#cp -R dave myboard (这是我取的板子名字，可以换上你的，但是后面的也要跟着来换哦^_^) <br>#cd myboard<br>#mv B2 myboards3c44b0 (自己取个板子名) <br>#cd myboards3c44b0 <br># mv B2.c myboards3c44b0.c <br>修改myboards3c44b0里面的Makefile, 把B2改成myboards3c44b0，编译时如果报的其它类似找不到B2的错误也是把相应的B2改成myboards3c44b0来处理。
<p>1）其中的myboards3c44b0.c文件是板的初始化代码，看一下就知道，根据你的板上自带的44binit.s来修改吧。memsetup.S文件主要是存储器的初始化设置，其实也是44binit.s里面的一部分。<br>2）其实board这里要修改的不多，先跳过flash部分吧，我们来看一下cpu设置部分吧。<br>u-boot-1.1.2里面已经加入了对s3c44b0的支持，让我们来看一下cpu/s3c44b0里面的部分吧。<br>看到start.S这个文件了吗？要修改的其实并不多，按照你的板设置一下中断跳转矢量就行了，或者上网查一下吧，应该很容易看明的。其中cpu.c这个文件简直不用修改，再来看一下serial.c这个文件吧，改一下波特率的设置就行，就是你用多少M的CPU频率的话对应的波特率参数设置问题，其实B2已经做得不错的了，很多子程序都不用自己写的了^_^，在我的板上是这样设置的,参考一下吧:<br>#if CONFIG_S3C44B0_CLOCK_SPEED==66 ，把所有的这些66改成60，原因，我的实验板上用的频率是60（44B0最高频率为64M）。然后其它分频系数，寄存器初始化设置，可以参考一下44blib.c，在这里我给出我的设置出来吧，可以参考一下。
<p>case 115200:<br>#if CONFIG_S3C44B0_CLOCK_SPEED==60<br>divisor = 32;
<p>UFCON0 = 0x0;<br>ULCON0 = 0x03;<br>UCON0 = 0x245;<br>UBRDIV0 = divisor;
<p>我在这方面算术不好，所以都是参考人家的。
<p>3）好，再加上个头文件：<br>cd u-boot-1.1.2/include/configs<br>cp B2.h myboards3c44b0.h<br>这个头文件其实要改的地方还挺多的，我等下再说怎样修改。先回到u-boot.1.1.2目录，
<p>4）在Makefile里面加上这部分，不会的话就模仿B2来写吧^_^<br>在B2的这部分文件<br>B2_config:unconfig<br>@./mkconfig $(@:_config=) arm s3c44b0 B2 dave<br>后面加上这部分：<br>myboards3c44b0_config:unconfig<br>@./mkconfig $(@:_config=) arm s3c44b0 myboard3c44b0 myboard
<p>切记在@./mkconfig $(@:_config=) arm s3c44b0 myboard3c44b0 myboard前面的是Tab来的，万万不能用空格代替，因为它是靠这个来识别命令的！<br>&nbsp; &nbsp;<br>5）好，现在可以在u-boot-1.1.2目录下执行如下命令：<br>Make distclean（还是用这个吧，比较彻底）<br>Make myboards3c44b0_config<br>Make<br>看看有没有错，有错的话按着提示来改，如果没错的话应该就能生成u-boot.bin这个文件，现在还不能用哦，因为这几是B2的一个仿制品而已（注：在我的机上到这一步可没有报错哦，你一步步按着来做的话应该也不会有什么错误吧^_^）。
<p><br>6）好，现在继续修改u-boot-1.1.2/include/configs/myboards3c44b0.h这个文件。<br>#define CONFIG_INIT_CRITICAL1 &nbsp; &nbsp; &nbsp; &nbsp; 这个在cpu/s3c44b0/start.S里面用到，如果你的u-boot程序不是在sdram中调试而是固化到flash中运行的话，这个必不可少。<br>找到 #define CONFIG_B2 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;1 &nbsp; &nbsp; &nbsp; &nbsp;把B2改成myboards3c44b0吧（不然就不会编译你的板了）<br>找到 #define CONFIG_S3C44B0_CLOCK_SPEED &nbsp; 75 &nbsp; 你的44b0x应该没有75M吧 我的是改成60的<br>找到Size of malloc() pool这部分设置，改成这样吧，反正我是照着人家来做的，你自己研究下吧^_^。
<p>#define CFG_MONITOR_LEN(256 * 1024)/* Reserve 256 kB for Monitor*/<br>#define CFG_ENV_SIZE &nbsp; (64*1024)/* 1024 bytes may be used for env vars*/<br>#define CFG_MALLOC_LEN(CFG_ENV_SIZE + 128*1024 )<br>#define CFG_GBL_DATA_SIZE128/* size in bytes reserved for initial data */<br>#define CFG_ENV_IS_IN_FLASH &nbsp;1 &nbsp;这个必不可少，如果你想把你的参数保存到flash的话（有些板是保存到EEPRAM中去的，但是s3c44b0的话还是保存到flash吧）<br>#define CFG_ENV_ADDR(PHYS_FLASH_1+0x40000) &nbsp;这个就是你的参数保存在flash里的起始地址了<br>#define CFG_ENV_OFFSET0x40000 &nbsp; &nbsp; &nbsp;这个我后来看它源程序发现如果你上一步没有设置它的起始地址的话就会用它来作默认地址的了<br>#define CONFIG_AUTO_COMPLETE
<p>其它地方没有深究哦，有些好像不要也行，你就试试吧。
<p>找到Hardware drivers部分，这应该是网络芯片设置吧，参考一下这个吧（要看芯片的）：<br>#define CONFIG_DRIVER_RTL8019 &nbsp; &nbsp; &nbsp; &nbsp;这个就要看你的板上用的是什么网卡了（这个是台湾出的，有10M）<br>#define RTL8019_BASE0x06000000 &nbsp; 这个是网卡相就寄存器的起始地址<br>以下部分我试过不要也行，你试下吧<br>#define RTL8019_BUS320<br>#define CONFIG_SMC_USE_16_BIT<br>#undef &nbsp;CONFIG_SHOW_ACTIVITY<br>#define CONFIG_NET_RETRY_COUNT10 &nbsp; 应该是重试的次数吧
<p>#define CONFIG_BAUDRATE115200 &nbsp;设置波特率
<p>#define CONFIG_COMMANDS( CONFIG_CMD_DFL | \<br>CFG_CMD_DATE | \<br>CFG_CMD_ELF| \<br>CFG_CMD_NET &nbsp; &nbsp;| \<br>CFG_CMD_EEPROM| \<br>CFG_CMD_I2C| \<br>CFG_CMD_FAT | \<br>CFG_CMD_JFFS2)<br>把CFG_CMD_EEPROM改成CFG_CMD_FLASH吧，虽然不改也是可以的，具体没考究。
<p>以下是板上env参数设置，看一下吧，其实我觉得了解一下就行的了，只是一些初始设置值，以后可以用命令setenv &nbsp;saveenv来修改的。<br>#define CONFIG_BOOTDELAY3 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;这个就是运行bootcmd之前的等待时间<br>#define CONFIG_BOOTARGS &nbsp;"devfs=mount root=ramfs console=ttyS0,115200" 引导uClinux的时候传递的参数，不会就先不用管它也行。 <br>#define CONFIG_ETHADDR00:50:c2:1e:af:fb &nbsp; &nbsp;网卡的物理地址MAC<br>#define CONFIG_NETMASK &nbsp;255.255.255.0 &nbsp; &nbsp; &nbsp; 掩码地址，设置过ip的人都应该知道吧<br>#define CONFIG_IPADDR &nbsp; 192.168.0.30 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;这是你板上网卡8019的ip地址<br>#define CONFIG_SERVERIP192.168.0.10 &nbsp; &nbsp; &nbsp; 这是你宿主机的ip地址，以后用tftp下载的时候用到，一定要跟你的宿主机一致才行。<br>#define CONFIG_BOOTFILE"u-boot.bin" &nbsp; &nbsp; &nbsp; &nbsp;这个就是你要下载文件的默认名字<br>#define CONFIG_BOOTCOMMAND"bootm 0x50000" &nbsp; 这是bootdelay后运行的命令
<p>Miscellaneous configurable options部分，参考一下吧：<br>#defineCFG_LONGHELP/* undef to save memory*/<br>#defineCFG_PROMPT"s3c44b0=&gt;" &nbsp; 这是进入命令模式下的提示符，改个帅一点的吧<br>#defineCFG_CBSIZE256/* Console I/O Buffer Size*/<br>#defineCFG_PBSIZE (CFG_CBSIZE+sizeof(CFG_PROMPT)+16) /* Print Buffer Size */<br>#defineCFG_MAXARGS 100/* max number of command args*/<br>#define CFG_BARGSIZECFG_CBSIZE/* Boot Argument Buffer Size*/
<p>#define CFG_MEMTEST_START &nbsp; &nbsp;0x0C400000/* memtest works on*/<br>#define CFG_MEMTEST_END0x0C800000/* 4 ... 8 MB in DRAM*/<br>#undef &nbsp;CFG_CLKS_IN_HZ/* everything, incl board info, in Hz */<br>#defineCFG_LOAD_ADDR0x0c008000默认的下载地址<br>#defineCFG_HZ1000/* 1 kHz */<br>#define CFG_BAUDRATE_TABLE &nbsp;{ 9600, 19200, 38400, 57600, 115200 } 可供选择的波特率
<p><br>Physical Memory Map部分，比较重要，修改你的sdram和flash的地址和容量。<br>#define CONFIG_NR_DRAM_BANKS1 &nbsp; 我们只占用了一个Bank用来映射sdram<br>#define PHYS_SDRAM_10x0c000000 &nbsp; &nbsp; sdram的起始地址<br>#define PHYS_SDRAM_1_SIZE0x00800000 &nbsp; &nbsp; sdram的容量（8M）<br>#define PHYS_FLASH_10x00000000 &nbsp; &nbsp; flash的起始地址<br>#define PHYS_FLASH_SIZEflash的容量（2M）<br>#define CFG_FLASH_BASEPHYS_FLASH_1 &nbsp; 定义多个名字而已，其它地方会用到
<p><br>FLASH organization部分，看注释应该知道了吧，参考一下：<br>#define CFG_FLASH_ERASE_TOUT4120000/* Timeout for Flash Erase (in ms)*/<br>#define CFG_FLASH_WRITE_TOUT4000/* Timeout for Flash Write (in ms)*/
<p><br>按着来一步步的做应该不难，这里提一下vi程序的用法，其实在google一搜就找到了，不过为了方便大家，还是说一声吧，<br>在命令模式下：<br>按/ xxx再回车即可搜索到你所需要的内容（xxx），再按n搜索下一个，按shift+n搜索上一个 <br>按:xxx即可跟到你想要的行<br>按:set nu显示行号<br>其它查书都可以找到就不说了。
<p>做到这个时候，你可以再编译一下，看有没有错，这一步我就没有帮你们做了，不过我觉得应该不会有问题吧，下载到ram中运行，看看效果。
<p><br>7）终于到了flash的设置部分了，这完全是我个人悟出来的，网上资料好像讲得不是先清楚。<br>&nbsp;首先介绍一下flash的识别吧，每块flash都有一个id，前部分用来说明生产厂家，后部分用来说明它的容量，类型，位数等。它们的定义是在u-boot-1.1.2/include/flash.h中，就以我的ssts9vf1601为例：<br>#define SST_MANUFACT0x00BF00BF &nbsp;这是生产厂家id<br>#define SST_ID_xF16010x234B234B &nbsp;这是它的型号，容量，位数等id<br>#define FLASH_SST160A0x0046 &nbsp; &nbsp; &nbsp; 这个我还说不清楚，有个要注意的问题是像sst160是不能用奇地址的，我做的过程中是把SST160A改成SST1601的，懒得帮它创一个<br>好了，相信你已经找到你的板上所用的flash对应的id号了吧？现在就来谈谈怎么改。<br>先进入u-boot-1.1.2/board/myboards3c44b0/common/flash.c<br>它里面已经帮SST160A设置好了，我的做法是把SST公司的160A都改成1601，是不是很简单，当然你也可以帮1601再写一分，但是我是个懒人，所以我就样做了^_^
<p>到这里还有个问题没提到的，就是如果你重新用setenv saveenv 设置了参数，但是复位后会发现怎么没有保存到的（不信你试试看）。其实我是故意留到这里讲的，应该它不会报错，可以说是比较隐秘的问题，所以我故意放到这里来说，以引起你的重视。<br>解决方法：vi u-boot-1.1.2/board/myboard/common/flash.c<br>找到这个函数：write_buff<br>里面有这句话：#ifdef CONFIG_B2<br>要把B2改了，因为我们的板不叫B2，那叫什么呢？原来是在这里定义的：u-boot-1.1.2/include/configs/myboards3c44b0.h<br>里面有这句话：#define CONFIG_HFRK 你不喜欢HFRK这个名字也行，可以改成别的名字，只不过相应地前面的B2也要改成这个名字而已。
<p><br>4.后记：<br>&nbsp; 本来是想刚做好的时候写的，但是后来发现有些功能实现不了，所以就停住了笔，等我搞好了以后，又急着去学习uClinux的移植，真的很忙，现在终于抽空把它写完。虽然有些地方还没说清楚（毕竟我对它的了解还不是很深，但我相信在我以后移植系统的过程中会逐步加深对它的了解），我还是希望它能成为最详尽的u-boot移植新手指导，对u-boot移植的初学者起到实质性的作用，这样就不枉我花了这么多的心思来写这个文档。<br><br>参考资料：<br><a href="http://blog.21ic.com/more.asp?name=sockit&amp;id=8509" target=_blank><font color=#0000ff>http://blog.21ic.com/more.asp?name=sockit&amp;id=8509</font></a><br>我所认为的最有奉献精神，写得最详细的记录，在此表示我深深的敬意！<br></p>
<table cellSpacing=0 cellPadding=0 width="100%">
    <tbody>
        <tr>
            <td><br></td>
        </tr>
    </tbody>
</table>
<br></span></div>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/36887.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-11-26 21:13 <a href="http://www.cnitblog.com/zouzheng/articles/36887.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>YAFFS文件系统移植笔记-</title><link>http://www.cnitblog.com/zouzheng/articles/36886.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Mon, 26 Nov 2007 13:01:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/36886.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/36886.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/36886.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/36886.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/36886.html</trackback:ping><description><![CDATA[v1.0,2005-6-6<br>一、YAFFS文件系统简介<br>YAFFS，Yet Another Flash File System，是一种类似于JFFS/JFFS2的专门为Flash设计的嵌入式文件系统。与JFFS相比，它减少了一些功能，因此速度更快、占用内存更少。<br>YAFFS和JFFS都提供了写均衡，垃圾收集等底层操作。它们的不同之处在于：<br>（1）、JFFS是一种日志文件系统，通过日志机制保证文件系统的稳定性。YAFFS仅仅借鉴了日志系统的思想，不提供日志机能，所以稳定性不如JAFFS，但是资源占用少。<br>（2）、JFFS中使用多级链表管理需要回收的脏块，并且使用系统生成伪随机变量决定要回收的块，通过这种方法能提供较好的写均衡，在YAFFS中是从头到尾对块搜索，所以在垃圾收集上JFFS的速度慢，但是能延长NAND的寿命。<br>（3）、JFFS支持文件压缩，适合存储容量较小的系统；YAFFS不支持压缩，更适合存储容量大的系统。<br>YAFFS还带有NAND芯片驱动，并为嵌入式系统提供了直接访问文件系统的API，用户可以不使用Linux中的MTD和VFS，直接对文件进行操作。NAND Flash大多采用MTD+YAFFS的模式。MTD（ Memory Technology Devices，内存技术设备）是对Flash操作的接口，提供了一系列的标准函数，将硬件驱动设计和系统程序设计分开。<br>二、YAFFS文件系统的移植<br>yaffs代码可以从<a href="http://www.aleph1.co.uk/armlinux/projects/"><a href="http://www.aleph1.co.uk/armlinux/projects/" target=_blank><font color=#000000>http://www.aleph1.co.uk/armlinux/projects/</font></a></a>下载（yaffs代码包括yaffs_ecc<br>.c，yaffs_fileem.c，yaffs_fs.c，yaffs_guts.c，yaffs_mtdif.c，yaffs_ramem.c。）<br>表一 Yaffs文件系统源代码相关文件及功能描述<br>文件名&nbsp;功&nbsp;&nbsp; 能<br>yaffs_ecc.c&nbsp;ECC校验算法<br>yaffs_fileem.c&nbsp;测试flash<br>yaffs_fs.c&nbsp;文件系统接口函数<br>yaffs_guts.c&nbsp;Yaffs文件系统算法<br>yaffs_mtdif.c&nbsp;NAND函数<br>yaffs_ramem.c&nbsp;Ramdisk实现<br>1.内核中没有YAFFS，所以需要自己建立YAFFS目录，并把下载的YAFFS代码复制到该目录下面。<br>#mkdir fs/yaffs<br>#cp *.c(yaffs source code) fs/yaffs<br>2.修改fs/Kconfig，使得可以配置yaffs :<br>source "fs/yaffs/Kconfig"<br>3．修改fs/makefile，添加如下内容：<br>obj-$(CONFIG_YAFFS_FS)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; += yaffs/<br>4.在fs目录下生成yaffs目录，并在里面生成一个makefile 和Kconfig<br>Makefile 内容为：<br>yaffs-objs := yaffs_fs.o yaffs_guts.o yaffs_mtdif.o yaffs_ecc.o <br>EXTRA_CFLAGS += $(YAFFS_CONFIGS) -DCONFIG_KERNEL_2_6<br>Kconfig内容为：<br>#<br># YAFFS file system configurations<br>#<br>config YAFFS_FS<br>&nbsp;tristate "Yet Another Flash Filing System(YAFFS) file system support"<br>&nbsp;help<br>&nbsp;&nbsp; YAFFS, for Yet Another Flash Filing System, is a filing system<br>&nbsp;&nbsp; optimised for NAND Flash chips.
<p>&nbsp;&nbsp; To compile the YAFFS file system support as a module, choose M here:<br>&nbsp;&nbsp; the module will be called yaffs.</p>
<p>&nbsp;&nbsp; If unsure, say N.</p>
<p>&nbsp;&nbsp; Further information on YAFFS is available at<br>&nbsp;&nbsp; &lt;<a href="http://www.aleph1.co.uk/yaffs/"><a href="http://www.aleph1.co.uk/yaffs/" target=_blank><font color=#000000>http://www.aleph1.co.uk/yaffs/</font></a></a>&gt;.</p>
<p>config YAFFS_MTD_ENABLED<br>&nbsp;bool "NAND mtd support"<br>&nbsp;depends on YAFFS_FS<br>&nbsp;help<br>&nbsp;&nbsp; This adds the yaffs file system support for working with a NAND mtd.</p>
<p>&nbsp;&nbsp; If unsure, say Y.</p>
<p>config YAFFS_RAM_ENABLED<br>&nbsp;bool "yaffsram file system support"<br>&nbsp;depends on YAFFS_FS<br>&nbsp;help<br>&nbsp;&nbsp; This adds the yaffsram file system support. Nice for testing on x86,<br>&nbsp;&nbsp; but uses 2MB of RAM.&nbsp; Don't enable for NAND-based targets.</p>
<p>&nbsp;&nbsp; If unsure, say N.</p>
<p>comment "WARNING: mtd and/or yaffsram support should be selected"<br>&nbsp;depends on YAFFS_FS &amp;&amp; !YAFFS_MTD_ENABLED &amp;&amp; !YAFFS_RAM_ENABLED</p>
<p>config YAFFS_USE_OLD_MTD<br>&nbsp;bool "Old mtd support"<br>&nbsp;depends on YAFFS_FS &amp;&amp; 0<br>&nbsp;help<br>&nbsp;&nbsp; Enable this to use the old MTD stuff that did not have yaffs support.<br>&nbsp;&nbsp; You can use this to get around compilation problems, but the best<br>&nbsp;&nbsp; thing to do is to upgrade your MTD support. You will get better speed.</p>
<p>&nbsp;&nbsp; If unsure, say N.</p>
<p>config YAFFS_USE_NANDECC<br>&nbsp;bool "Use ECC functions of the generic MTD-NAND driver"<br>&nbsp;depends on YAFFS_FS<br>&nbsp;default y<br>&nbsp;help<br>&nbsp;&nbsp; This enables the ECC functions of the generic MTD-NAND driver.<br>&nbsp;&nbsp; This will not work if you are using the old mtd.</p>
<p>&nbsp;&nbsp; NB Use NAND ECC does not work at present with yaffsram.</p>
<p>&nbsp;&nbsp; If unsure, say Y.</p>
<p>config YAFFS_ECC_WRONG_ORDER<br>&nbsp;bool "Use the same ecc byte order as Steven Hill's nand_ecc.c"<br>&nbsp;depends on YAFFS_FS<br>&nbsp;help<br>&nbsp;&nbsp; This makes yaffs_ecc.c use the same ecc byte order as<br>&nbsp;&nbsp; Steven Hill's nand_ecc.c. If not set, then you get the<br>&nbsp;&nbsp; same ecc byte order as SmartMedia.</p>
<p>&nbsp;&nbsp; If unsure, say N.</p>
<p>config YAFFS_USE_GENERIC_RW<br>&nbsp;bool "Use Linux file caching layer"<br>&nbsp;default y<br>&nbsp;depends on YAFFS_FS<br>&nbsp;help<br>&nbsp;&nbsp; Use generic_read/generic_write for reading/writing files. This<br>&nbsp;&nbsp; enables the use of the Linux file caching layer.</p>
<p>&nbsp;&nbsp; If you disable this, then caching is disabled and file read/write<br>&nbsp;&nbsp; is direct.</p>
<p>&nbsp;&nbsp; If unsure, say Y.</p>
<p>config YAFFS_USE_HEADER_FILE_SIZE<br>&nbsp;bool "Use object header size"<br>&nbsp;depends on YAFFS_FS<br>&nbsp;help<br>&nbsp;&nbsp; When the flash is scanned, two file sizes are constructed:<br>&nbsp;&nbsp; * The size taken from the object header for the file.<br>&nbsp;&nbsp; * The size figured out by scanning the data chunks.<br>&nbsp;&nbsp; If this option is enabled, then the object header size is used,<br>&nbsp;&nbsp; otherwise the scanned size is used.</p>
<p>&nbsp;&nbsp; If unsure, say N.</p>
<p>config YAFFS_DISABLE_CHUNK_ERASED_CHECK<br>&nbsp;bool "Turn off debug chunk erase check"<br>&nbsp;depends on YAFFS_FS<br>&nbsp;default y<br>&nbsp;help<br>&nbsp;&nbsp; Enabling this turns off the test that chunks are erased in flash<br>&nbsp;&nbsp; before writing to them.&nbsp; This is safe, since the write verification<br>&nbsp;&nbsp; will fail.&nbsp; Suggest enabling the test (ie. say N)<br>&nbsp;&nbsp; during development to help debug things.</p>
<p>&nbsp;&nbsp; If unsure, say Y.</p>
<p>#config YAFFS_DISABLE_WRITE_VERIFY<br>#&nbsp;bool "Disable write verify (DANGEROUS)"<br>#&nbsp;depends on YAFFS_FS &amp;&amp; EXPERIMENTAL<br>#&nbsp;help<br>#&nbsp;&nbsp; I am severely reluctant to provide this config. Disabling the<br>#&nbsp;&nbsp; verification is not a good thing to do since NAND writes can<br>#&nbsp;&nbsp; fail silently.&nbsp; Disabling the write verification will cause your<br>#&nbsp;&nbsp; teeth to rot, rats to eat your corn and give you split ends.<br>#&nbsp;&nbsp; You have been warned. ie. Don't uncomment the following line.<br>#<br>#&nbsp;&nbsp; If unsure, say N.<br>#</p>
<p>config YAFFS_SHORT_NAMES_IN_RAM<br>&nbsp;bool "Cache short names in RAM"<br>&nbsp;depends on YAFFS_FS<br>&nbsp;default y<br>&nbsp;help<br>&nbsp;&nbsp; If this config is set, then short names are stored with the<br>&nbsp;&nbsp; yaffs_Object.&nbsp; This costs an extra 16 bytes of RAM per object,<br>&nbsp;&nbsp; but makes look-ups faster.</p>
<p>&nbsp;&nbsp; If unsure, say Y.<br>5．在/arch/arm/mach-s3c2410/mach-smdk2410.c找到smdk_default_nand_part结构，修改nand分区，如下：<br>struct mtd_partition smdk_default_nand_part[] = {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [0] = {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .name&nbsp;&nbsp; = "vivi",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .size&nbsp;&nbsp; = 0x00020000,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .offset = 0x00000000,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; },<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [1] = {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .name&nbsp;&nbsp; = "param",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .size&nbsp;&nbsp; = 0x00010000,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .offset = 0x00020000,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; },<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [2] = {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .name&nbsp;&nbsp; = "kernel",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .size&nbsp;&nbsp; = 0x00100000,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .offset = 0x00030000,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; },<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [3] = {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .name&nbsp;&nbsp; = "root",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .size&nbsp;&nbsp; = 0x01900000,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .offset = 0x00130000,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; },<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [4] = {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .name&nbsp;&nbsp; = "user",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .size&nbsp;&nbsp; = 0x025d0000,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .offset = 0x01a30000,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>};<br>注：此分区要结合vivi里面的分区来进行设置。<br>6．配置内核时选中MTD支持：<br>Memory Technology Devices (MTD)&nbsp; ---&gt;<br>&nbsp;&lt;*&gt; Memory Technology Device (MTD) support<br>&nbsp;&nbsp;[*]&nbsp;&nbsp; MTD partitioning support<br>&nbsp;&nbsp;&nbsp;&#8230;&#8230;<br>&nbsp;&nbsp;--- User Modules And Translation Layers<br>&nbsp;&nbsp;&lt;*&gt; Direct char device access to MTD devices<br>&nbsp;&nbsp;&lt;*&gt; Caching block device access to MTD devices<br>&nbsp;&nbsp;&#8230;&#8230;<br>NAND Flash Device Drivers&nbsp; ---&gt;<br>&nbsp;&lt;*&gt; NAND Device Support<br>&nbsp;&lt;*&gt; NAND Flash support for S3C2410 SoC<br>&nbsp;[*]&nbsp;&nbsp; S3C2410 NAND driver debug&nbsp; <br>7．配置内核时选中YAFFS支持：<br>File systems&nbsp; ---&gt;<br>&nbsp;Miscellaneous filesystems&nbsp; ---&gt;<br>&nbsp;&nbsp;&lt;*&gt; Yet Another Flash Filing System(YAFFS) file system support<br>&nbsp;&nbsp;[*]&nbsp;&nbsp; NAND mtd support<br>&nbsp;&nbsp;[*]&nbsp;&nbsp; Use ECC functions of the generic MTD-NAND driver<br>&nbsp;&nbsp;[*]&nbsp;&nbsp; Use Linux file caching layer<br>&nbsp;&nbsp;[*]&nbsp;&nbsp; Turn off debug chunk erase check<br>&nbsp;&nbsp;[*]&nbsp;&nbsp; Cache short names in RAM<br>8.编译内核并将内核下载到开发板的flash中。<br>三、Yaffs文件系统测试：<br>1.内核启动之后，在启动信息里面可以看到如下内容：<br>NAND device: Manufacturer ID: 0xec, Chip ID: 0x76 (Samsung NAND 64MiB 3,3V 8-bit)<br>Scanning device for bad blocks<br>Creating 5 MTD partitions on "NAND 64MiB 3,3V 8-bit":<br>0x00000000-0x00020000 : "vivi"<br>0x00020000-0x00030000 : "param"<br>0x00030000-0x00130000 : "kernel"<br>0x00130000-0x01a30000 : "root"<br>0x01a30000-0x04100000 : "user"<br>2.如果在内核里面添加了proc文件系统的支持那么你在proc里面可以看到有关yaffs的信息<br>~ # cat proc/filesystems<br>nodev&nbsp;&nbsp; sysfs<br>nodev&nbsp;&nbsp; rootfs<br>nodev&nbsp;&nbsp; bdev<br>nodev&nbsp;&nbsp; proc<br>nodev&nbsp;&nbsp; sockfs<br>nodev&nbsp;&nbsp; pipefs<br>nodev&nbsp;&nbsp; futexfs<br>nodev&nbsp;&nbsp; tmpfs<br>nodev&nbsp;&nbsp; eventpollfs<br>nodev&nbsp;&nbsp; devpts<br>nodev&nbsp;&nbsp; ramfs<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; vfat<br>nodev&nbsp;&nbsp; devfs<br>nodev&nbsp;&nbsp; nfs<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; yaffs<br>nodev&nbsp;&nbsp; rpc_pipefs<br>3.查看dev目录下相关目录可以看到：<br>~ # ls dev/mtd -al<br>drwxr-xr-x&nbsp;&nbsp;&nbsp; 1 root&nbsp;&nbsp;&nbsp;&nbsp; root&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0 Jan&nbsp; 1 00:00 .<br>drwxr-xr-x&nbsp;&nbsp;&nbsp; 1 root&nbsp;&nbsp;&nbsp;&nbsp; root&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0 Jan&nbsp; 1 00:00 ..<br>crw-rw-rw-&nbsp;&nbsp;&nbsp; 1 root&nbsp;&nbsp;&nbsp;&nbsp; root&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 90,&nbsp;&nbsp; 0 Jan&nbsp; 1 00:00 0<br>cr--r--r--&nbsp;&nbsp;&nbsp; 1 root&nbsp;&nbsp;&nbsp;&nbsp; root&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 90,&nbsp;&nbsp; 1 Jan&nbsp; 1 00:00 0ro<br>crw-rw-rw-&nbsp;&nbsp;&nbsp; 1 root&nbsp;&nbsp;&nbsp;&nbsp; root&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 90,&nbsp;&nbsp; 2 Jan&nbsp; 1 00:00 1<br>cr--r--r--&nbsp;&nbsp;&nbsp; 1 root&nbsp;&nbsp;&nbsp;&nbsp; root&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 90,&nbsp;&nbsp; 3 Jan&nbsp; 1 00:00 1ro<br>crw-rw-rw-&nbsp;&nbsp;&nbsp; 1 root&nbsp;&nbsp;&nbsp;&nbsp; root&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 90,&nbsp;&nbsp; 4 Jan&nbsp; 1 00:00 2<br>cr--r--r--&nbsp;&nbsp;&nbsp; 1 root&nbsp;&nbsp;&nbsp;&nbsp; root&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 90,&nbsp;&nbsp; 5 Jan&nbsp; 1 00:00 2ro<br>crw-rw-rw-&nbsp;&nbsp;&nbsp; 1 root&nbsp;&nbsp;&nbsp;&nbsp; root&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 90,&nbsp;&nbsp; 6 Jan&nbsp; 1 00:00 3<br>cr--r--r--&nbsp;&nbsp;&nbsp; 1 root&nbsp;&nbsp;&nbsp;&nbsp; root&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 90,&nbsp;&nbsp; 7 Jan&nbsp; 1 00:00 3ro<br>crw-rw-rw-&nbsp;&nbsp;&nbsp; 1 root&nbsp;&nbsp;&nbsp;&nbsp; root&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 90,&nbsp;&nbsp; 8 Jan&nbsp; 1 00:00 4<br>cr--r--r--&nbsp;&nbsp;&nbsp; 1 root&nbsp;&nbsp;&nbsp;&nbsp; root&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 90,&nbsp;&nbsp; 9 Jan&nbsp; 1 00:00 4ro</p>
<p>~ # ls dev/mtdblock/ -al<br>drwxr-xr-x&nbsp;&nbsp;&nbsp; 1 root&nbsp;&nbsp;&nbsp;&nbsp; root&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0 Jan&nbsp; 1 00:00 .<br>drwxr-xr-x&nbsp;&nbsp;&nbsp; 1 root&nbsp;&nbsp;&nbsp;&nbsp; root&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0 Jan&nbsp; 1 00:00 ..<br>brw-------&nbsp;&nbsp;&nbsp; 1 root&nbsp;&nbsp;&nbsp;&nbsp; root&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 31,&nbsp;&nbsp; 0 Jan&nbsp; 1 00:00 0<br>brw-------&nbsp;&nbsp;&nbsp; 1 root&nbsp;&nbsp;&nbsp;&nbsp; root&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 31,&nbsp;&nbsp; 1 Jan&nbsp; 1 00:00 1<br>brw-------&nbsp;&nbsp;&nbsp; 1 root&nbsp;&nbsp;&nbsp;&nbsp; root&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 31,&nbsp;&nbsp; 2 Jan&nbsp; 1 00:00 2<br>brw-------&nbsp;&nbsp;&nbsp; 1 root&nbsp;&nbsp;&nbsp;&nbsp; root&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 31,&nbsp;&nbsp; 3 Jan&nbsp; 1 00:00 3<br>brw-------&nbsp;&nbsp;&nbsp; 1 root&nbsp;&nbsp;&nbsp;&nbsp; root&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 31,&nbsp;&nbsp; 4 Jan&nbsp; 1 00:00 4<br>4.mount、umount<br>建立mount目录<br>~ #mkdir /mnt/flash0<br>~ #mkdir /mnt/flash1<br>Mountblockdevice设备<br>~ #mount &#8211;t yaffs /dev/mtdblock/3 /mnt/flash0<br>~ #mount &#8211;t yaffs /dev/mtdblock/4 /mnt/flash1<br>~ #cp 1.txt /mnt/flash0<br>~ #cp 2.txt /mnt/flash1<br>查看mount上的目录，可以看到该目录下有刚才拷贝的文件，将其umount后，再次mount上来可以发现拷贝的文件仍然存在，这时删除该文件然后umount，再次mount后，可以发现拷贝的文件已经被删除，由此可以该分区可以正常读写。<br>5.在flash上建立根文件系统<br>~ # mount &#8211;t yaffs /dev/mtdblock/3 /mnt/flash0<br>~ #cp (your rootfs) /mnt/flash0<br>~ #umount /mnt/flash0<br>重新启动，改变启动参数：<br>param set linux_cmd_line "noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0"<br>重新启动，开发板就可以从flash启动根文件系统了。<br>注：这里你得在内核中添加devfs文件系统的支持，否则内核无法找到/dev/mtdblock/3目录</p>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/36886.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-11-26 21:01 <a href="http://www.cnitblog.com/zouzheng/articles/36886.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何在44B0开发板上建立基于Nandflash的JFFS2文件系统</title><link>http://www.cnitblog.com/zouzheng/articles/36885.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Mon, 26 Nov 2007 12:58:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/36885.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/36885.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/36885.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/36885.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/36885.html</trackback:ping><description><![CDATA[<div class=tpc_content>【转载】如何在44B0开发板ARMSYS上建立基于Nandflash的JFFS2文件系统 <br>【作者】杭州立宇泰电子有限公司 <br>【原文】<a href="http://www.hzlitai.com.cn/" target=_blank><font color=#000000>www.hzlitai.com.cn</font></a> <br><br>本文描述了如何基于一款以S3C44B0X为核心的ARMSYS开发板的基础上建立Nandflash的MTD（Memory Technology Devices）驱动和JFFS2 (Journaling Flash File System2)文件系统。 <br><br>本文参考资料： <br>&nbsp; 有关MTD和JFFS2的基础知识：<a href="http://www.linux-mtd.infradead.org/tech/nand.html" target=_blank><font color=#000000>http://www.linux-mtd.infradead.org/tech/nand.html</font></a> <br>&nbsp; uClinux下MTD和JFFS2在M5407C3开发板上的移植： <br>&nbsp; <a href="http://www.enseirb.fr/~kadionik/embedded/uclinux/mtd/howto_mtd.html" target=_blank><font color=#000000>http://www.enseirb.fr/~kadionik/embedded/uclinux/mtd/howto_mtd.html</font></a> <br><br>1．概述 <br><br>1．1关于Nandflash <br>&nbsp; 以S3C44B0X为核心的ARMSYS开发板采用的Nandflash是三星公司的K9F2808芯片。它的存储空间以页为单位。1页是由512字节的数据和16字节的备用空间组成（备用空间可以用来存储ECC（纠错码），坏块信息和文件系统相关的数据）。这里我们仅考虑数据空间即可。因此可以认为K9F2808每页大小为512字节。32页组成一个块，因此块的大小为16K（0x4小空间为0x4000字节。 <br>&nbsp; 对芯片的读/写/擦除命令的写入都是通过置高CLE引脚同时向I/O0~I/O7写入命令代码字节来完成。地址的写入则是通过置高ALE引脚同时写入地址字节来完成。对Nandflash的操作仅需要对几条信号线进行控制即可完成了。 <br>&nbsp; /RE和 /WE信号线则可以由相应的bank选择线与CPU的/OE和/WE逻辑与来控制。在ARMSYS上是采用bank1选择线，因此Nandflash在系统中的映射地址是0x200000。 <br>I/O 0-7联接到CPU的数据总线D0-D7(D15). /WP直接接VCC而/SE直接接地即可。 <br>&nbsp; R/B，/CE，CLE ，ALE和都应当联接CPU的GPIO引脚。ARMSYS中采用了PC0~PC3。 <br>以上都是对我们移植MTD有用的信息。可以通过查看K9F2808的Datasheet来了解关于该器件更详细的信息。 <br><br>1．2关于支持Nandflash的文件系统 <br><br>&nbsp; 现在仅有少量的文件系统支持各种类型的Nandflash设备：JFFS2 和 YAFFS 支持 NAND Flash芯片和 SmartMediaCard(智能多媒体卡)NTFL支持 DiskOnChip 设备 来自 M-Systems的TRUEFFS支持 DiskOnChip设备由SSFDC论坛定义的SmartMedia(智能多媒体卡)的 DOS-FAT 文件系统 &nbsp; 其中JFFS2 是一个开源的文件系统。JFFS2支持原始的NAND芯片同时支持SmartMediaCard。关于JFFS2的详细信息可以查看<a href="http://sources.redhat.com/jffs2/" target=_blank><font color=#000000>http://sources.redhat.com/jffs2/</font></a> 。JFFS2还提供文件的压缩和解压服务，这对小型的flash很有用处。JFFS2中包括了对坏块的管理，纠错并提供在Nandflash上使用的可用于工业用途的可靠稳定的文件系统。 <br><br>1．3软件层次 <br><br>JFFS2的建立包括4个层次：a) JFFS2：文件系统驱动 <br>b) MTD：存储技术设备驱动Memory Technology Devices driver <br>c) NAND：通用Nand驱动 <br>d) 特定的硬件驱动其中MTD驱动提供给JFFS2一个挂载点。通用NAND驱动提供必要的识别、读、写和擦除功能。与特定的硬件相关的功能函数则由底层的硬件驱动提供。2．准备工作 <br><br>2．1解压uClinux移植包 <br><br>我们还是采用uClinux在ARMSYS上的移植包：uClinux-ARMSYS-20040801.tar.gz来完成JFFS2的移植的工作。 <br>uClinux-ARMSYS-20040801.tar.gz是移植自uClinux-dist-20040408.tar.gz，在很多方面比早先的20030522版本要完善很多，这也使我们的工作变得方便很多。这里我们使用的内核版本是Linux 2.4.24。 <br>将uClinux-ARMSYS-20040801.tar.gz拷贝到/home/下，运行解压：tar xvzf uClinux-ARMSYS-20040801.tar.gz解压结束后会在/home/下生成uClinux-dist目录。2．2安装编译环境 <br>将arm-elf-tools-20030314.sh拷贝到根目录，运行安装：sh arm-elf-tools-20030314.sh3．修改drivers/mtd/nand/下的几个文件 <br><br>3．1修改Config.in <br><br>&nbsp; 在文件Linux-2.4.x/drivers/mtd/nand/Config.in查找到CONFI_CPU_S3C44B0X，在其中加入一行（以下用粗体显示）对于ARMSYS开发板的选项：if [ "$CONFIG_CPU_S3C44B0X" = "y" ]; then <br>dep_tristate ' NAND Flash device on FS44B0-CORE V2.00 board' CONFIG_MTD_NAND_S3C44B0X $CONFIG_MTD_NAND <br>dep_tristate ' NAND Flash device on ARMSYS board' CONFIG_MTD_NAND_ARMSYS $CONFIG_MTD_NAND <br>fi <br>这样在后面进行配置（make menuconfig）时就可以看到关于ARMSYS的选项了。 <br><br>3．2修改Makefile <br><br>在文件Linux-2.4.x/drivers/mtd/nand/Makefile中，加入一行（以下用粗体显示）：# <br># linux/drivers/nand/Makefile <br># <br># $Id: Makefile,v 1.10 2002/12/01 13:23:05 gleixner Exp $O_TARGET := nandlink.o <br>export-objs := nand.o nand_ecc.o nand_ids.o <br>obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o <br>obj-$(CONFIG_MTD_NAND_SPIA) += spia.o <br>obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o <br>obj-$(CONFIG_MTD_NAND_EDB7312) += edb7312.o <br>obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o <br>obj-$(CONFIG_MTD_NAND_S3C44B0X) += s3c44b0x.o <br>obj-$(CONFIG_MTD_NAND_ARMSYS) += armsys_44b0x.o <br>include $(TOPDIR)/Rules.make <br>这样在运行对内核的Make时，就会编译我们待会要加入的armsys_44b0x.c文件了。3．3加入armsys_44b0x.c文件 <br><br>&nbsp; 在Linux-2.4.x/drivers/mtd/nand/目录下加入armsys_44b0x.c文件。该文件可以在这里下载。armsys_44b0x.c文件是基于同一目录中的其它文件修改的。例如spia.c文件。armsys_44b0x.c只需要根据ARMSYS的硬件电路（参考第1.1节）在上面做一些修改就可以了。 <br>主要做了修改的几处如下： <br><br>第一处， <br>#define ARMSYS_IO_BASE 0x01d20000 /*GPIO相关寄存器的起始地址*/ <br>#define ARMSYS_FIO_BASE 0x2000000 /*Nandflash映射的起始地址*/ <br>#define ARMSYS_PEDR 0x0014 <br>/* 控制CLE,ALE和NCE脚的port C的数据寄存器相对于GPIO寄存器起始地址的偏移量*/ <br>#define ARMSYS_PEDDR 0x0010 <br>/*控制CLE,ALE和NCE脚的port C的控制寄存器相对于GPIO寄存器起始地址的偏移量*/ <br><br>第二处， <br>#ifdef CONFIG_MTD_PARTITIONS <br>/* <br>* 定义flash设备的分区 <br>*/ <br>const static struct mtd_partition partition_info[] = { <br>{ name: "ARMSYS flash partition 1", <br>offset: 2*1024*1024, <br>size: 6*1024*1024 }, <br>{ name: "ARMSYS flash partition 2", <br>offset: 8*1024*1024, <br>size: 6*1024*1024 } <br>}; <br>#define NUM_PARTITIONS 2 <br>#endif <br><br>第三处， <br>/* <br>* 特定硬件的控制线操作 <br>*/ <br>void armsys_hwcontrol(int cmd){switch(cmd){ <br>case NAND_CTL_SETCLE: (*(volatile unsigned *) (armsys_io_base + armsys_pedr)) |= 0x04; break; <br>case NAND_CTL_CLRCLE: (*(volatile unsigned *) (armsys_io_base + armsys_pedr)) &amp;= ~0x04; break; <br>case NAND_CTL_SETALE: (*(volatile unsigned *) (armsys_io_base + armsys_pedr)) |= 0x08; break; <br>case NAND_CTL_CLRALE: (*(volatile unsigned *) (armsys_io_base + armsys_pedr)) &amp;= ~0x08; break; <br>case NAND_CTL_SETNCE: (*(volatile unsigned *)(armsys_io_base + armsys_pedr)) &amp;= ~0x02; break; <br>case NAND_CTL_CLRNCE: (*(volatile unsigned *) (armsys_io_base + armsys_pedr)) |= 0x02; break; <br>&nbsp; } <br>} <br><br>第四处， <br>/*设置GPIO Port C 的控制寄存器从而使得Nandflash对应的控制引脚的输入/输出配置正确 */ <br>(*(volatile unsigned *) (armsys_io_base + armsys_peddr)) = 0x0f00ff54;4．添加mtdX和mtdblockX设备 <br>修改/vendors/Samsung/44B0/Makefile文件。 <br>在DEVICES = \ <br>&#8230;&#8230;&#8230; <br>的最后加上： <br>(Tab)\ <br>(Tab)mtd0,c,90,0(Tab)mtd1,c,90,2 <br>(Tab)\ <br>(Tab)mtdblock0,b,30,0(Tab)mtdblock1,b,30,1这里(Tab)表示输入Tab键 <br><br>5．配置内核 <br><br>下面就可以开始配置内核和用户选项了。 <br>打开终端。 <br># cd /home/uClinux-dist <br># make menuconfig进入uClinux配置(uClinux v3.1.0 Configuration)，选中&#8220;Kernel/Library/Defaults Selection&#224;&#8221;敲空格进入。选中内核设置项和用户选项： <br><br>Customize Kernel Settings <br>Customize Vendor/User Settings <br>建议通过直接载入我们提供的内核配置文件Config_Kernel（点击这里下载）和用户配置文件Config_User（点击这里下载）来完成配置。将Config_Kernel拷贝到uClinux-dist/Linux-2.4.x目录下，将Config_User拷贝到uClinux-dist/Config目录下。回到终端，按下ESC键两次，敲回车退出。进入内核配置（Linux Kernel v2.4.24-uc0 Configuration），选中&#8220;Load an Alternate Configuration File&#8221;，敲空格键进入，输入Config_Kernel文件名，按回车退出。内核选项就被设置好了。按下ESC键，敲回车保存设置。自动切换到用户选项配置。同样选中&#8220;Load an Alternate Configuration File&#8221;，敲空格键进入，输入Config_User文件名，按回车退出。用户选项就被设置好了。也可以手动修改。 <br>手动修改的步骤如下： <br>进入内核配置（Linux Kernel v2.4.24-uc0 Configuration）。 <br><br>5．1选择支持可载入的模块 <br><br>选中&#8220;Loadable mudule support&#224;&#8221;，敲空格进入。选中第1项： <br>Enable loadable module support <br>按ESC退出。 <br><br>5．2对MTD进行配置 <br><br>选中&#8220;Memory Technology Device (MTD)--&gt;&#8221;，敲空格进入。需要选中的项目如下：&lt;*&gt; Memory Technology Device (MTD) support <br>Debugging <br>(0) Debugging verbosity (0= quiet, 3=noisy) <br>&lt;*&gt; MTD partitioning support <br>&#8230;&#8230;&#8230; <br>--- User Modules And Translation Layers <br>&lt;*&gt; Direct char device access to MTD devices <br>&lt;*&gt; Caching block device access to MTD devices选中&#8220;NAND Flash Device Drivers -&#224;&#8221;，敲空格进入。需要选中的项目如下：&lt;*&gt; NAND Device Support <br>&#8230;&#8230;&#8230; <br>&lt;*&gt; NAND Flash device on ARMSYS board <br>这一条就是我们修改前面的Config.in文件的结果。 <br>敲两次ESC退出MTD配置。5．3对文件系统进行配置 <br>选中&#8220;File system --&gt;&#8221;，敲空格进入。需要选中的项目如下： <br>&lt;*&gt; Journalling Flash File System v2 (JFFS2) support <br>(0) JFFS2 debugging verbosity (0 = quiet, 2 = noisy ) <br>敲两次ESC，敲回车，退出内核配置。6．配置用户选项 <br><br>进入用户选项配置。 <br><br>6．1对MTD工具进行配置 <br><br>选中&#8220;Flash Tools--&gt;&#8221;，敲空格键进入。需要选中的项目如下： <br>mtd-utils <br>erase <br>eraseall <br>&#8230;&#8230;&#8230; <br>mkfs.jffs2 <br>敲ESC键退出。 <br><br>6．2对BusyBox进行配置 <br><br>选中&#8220;BusyBox--&gt;&#8221;，敲空格键进入。需要选中的项目如下： <br>&#8230;&#8230;&#8230; <br>mount <br>&#8230;&#8230;&#8230; <br>umount <br>&#8230;&#8230;&#8230; <br>vi <br>敲2次ESC键，并回车保存。 <br>到此为之，所需的配置选项就全部设好了。下面就开始编译了。 <br><br>7．编译uClinux <br><br>按下面的步骤进行编译：# make dep <br># make lib_only <br># make user_only <br># make romfs <br># make image <br># make编译成功后，在uClinux-dist/目录下产生images目录，其中的3个文件：image.ram, image.rom和romfs.img就是我们可以使用的二进制文件。 <br><br>8．下载与启动 <br><br>8．1下载 <br><br>利用ARMSYS提供的Bootloader通过USB接口下载2个二进制文件：l image.ram（文件改名为imageram.bin）下载到地址0x0c008000； <br>l romfs.img（文件改名为romfs.img）下载到地址0x0c600000； <br><br>8．2启动 <br><br>从0xc008000地址处开始运行，可以从超级终端上观察到如下所示的输出信息：Linux version 2.4.24-uc0 (root@localhost) (gcc version 2.95.3 20010315 (release) <br>(ColdFire patches - 20010318 from <a href="http://fiddes.net/coldfire/" target=_blank><font color=#000000>http://fiddes.net/coldfire/</font></a>)(uClinux XIP and shared lib patches from <a href="http://www.snapgear.com/" target=_blank><font color=#000000>http://www.snapgear.com/</font></a>)) #108 六 9月 25 20:11:04 CST 2004 <br>Processor: Samsung S3C44B0X revision 0 <br>Architecture: S3C44B0X <br>On node 0 totalpages: 2048 <br>zone(0): 0 pages. <br>zone(1): 2048 pages. <br>zone(2): 0 pages. <br>Kernel command line: root=/dev/rom0 init=/linuxrc <br>Calibrating delay loop... 31.84 BogoMIPS <br>Memory: 8MB = 8MB total <br>Memory: 6900KB available (970K code, 152K data, 36K init) <br>Dentry cache hash table entries: 1024 (order: 1, 8192 bytes) <br>Inode cache hash table entries: 512 (order: 0, 4096 bytes) <br>Mount cache hash table entries: 512 (order: 0, 4096 bytes) <br>Buffer cache hash table entries: 1024 (order: 0, 4096 bytes) <br>Page-cache hash table entries: 2048 (order: 1, 8192 bytes) <br>POSIX conformance testing by UNIFIX <br>Linux NET4.0 for Linux 2.4 <br>Based upon Swansea University Computer Society NET3.039 <br>Starting kswapd <br>JFFS2 version 2.1. (C) 2001 Red Hat, Inc., designed by Axis Communications AB. <br>ttyS0 at I/O 0x1d00000 (irq = 3) is a S3C44B0 <br>ttyS1 at I/O 0x1d04000 (irq = 2) is a S3C44B0 <br>Blkmem copyright 1998,1999 D. Jeff Dionne <br>Blkmem copyright 1998 Kenneth Albanowski <br>Blkmem 1 disk images: <br>0: C600000-C65FFFF [VIRTUAL C600000-C65FFFF] (RO) <br>RAMDISK driver initialized: 16 RAM disks of 1024K size 1024 blocksize <br>NAND device: Manufacture ID: 0xec, Chip ID: 0x73 (Samsung NAND 16MB 3,3V) <br>Creating 2 MTD partitions on "NAND 16MB 3,3V": <br>0x00200000-0x00800000 : "ARMSYS flash partition 1" <br>mtd: Giving out device 0 to ARMSYS flash partition 1 <br>0x00800000-0x00e00000 : "ARMSYS flash partition 2" <br>mtd: Giving out device 1 to ARMSYS flash partition 2 <br>VFS: Mounted root (romfs filesystem) readonly. <br>Freeing init memory: 36K <br>Shell invoked to run file: /etc/rc <br>Command: hostname Samsung <br>Command: /bin/expand /etc/ramfs.img /dev/ram0 <br>Command: mount -t proc proc /proc <br>Command: mount -t ext2 /dev/ram0 /var <br>Command: mkdir /var/config <br>Command: mkdir /var/tmp <br>Command: mkdir /var/log <br>Command: mkdir /var/run <br>Command: mkdir /var/lock <br>Command: cat /etc/motd <br>Welcome to <br>____ _ _ <br>/ __| ||_| <br>_ _| | | | _ ____ _ _ _ _ <br>| | | | | | || | _ \| | | |\ \/ / <br>| |_| | |__| || | | | | |_| |/ \ <br>| ___\____|_||_|_| |_|\____|\_/\_/ <br>| | <br>|_|For further information check: <br><a href="http://www.uclinux.org/" target=_blank><font color=#000000>http://www.uclinux.org/</font></a> <br>Command: <br>Execution Finished, Exiting <br>Sash command shell (version 1.1.1) <br>/&gt; <br>其中的黑体部分说明系统已经识别到了K9F2808器件，并创建了2个MTD分区。还可以到proc目录中看一下MTD分区表： <br>/&gt; cd proc <br>/proc&gt; cat mtd <br>dev: size erasesize name <br>mtd0: 00600000 00004000 "ARMSYS flash partition 1" <br>mtd1: 00600000 00004000 "ARMSYS flash partition 2" <br>/proc&gt;到dev目录下查看一下mtd设备： <br>/proc&gt; cd /dev <br>/dev&gt; ls <br>console <br>cua0 <br>cua1 <br>kmem <br>mem <br>mtd0 <br>mtd1 <br>mtdblock0 <br>mtdblock1 <br>null <br>ptyp0 <br>&#8230;&#8230;&#8230; <br>urandom <br>zero <br>/dev&gt; <br>有了上面的结果就说明MTD设备已经配置好了。下面我们就要创建一个JFFS2映像文件，并将它拷贝到MTD分区中。 <br><br>9．创建和拷贝JFFS2映像文件 <br><br>在目标板上运行以下的命令行： <br>/dev&gt; cd /var/tmp <br>/var/tmp&gt; mkdir jffs2 <br>/var/tmp&gt; c <br>/var/tmp/jffs2&gt; vi file1 <br>Hello, this is JFFS2 on Nandflash. <br>/var/tmp/jffs2&gt; cat file1 <br>Hello, this is JFFS2 on Nandflash. <br>/var/tmp/jffs2&gt; mkdir folder1 <br>/var/tmp/jffs2&gt; mkdir folder2 <br>/var/tmp/jffs2&gt; mkdir folder3 <br>/var/tmp/jffs2&gt; cd .. <br>/var/tmp&gt; mkfs.jffs2 -d jffs2 -o jffs2.img <br>/var/tmp&gt; ls <br>jffs2 <br>jffs2.img <br>/var/tmp&gt;eraseall /dev/mtd0 <br>&#8230;&#8230;&#8230; <br>/var/tmp&gt; cp jffs2.img /dev/mtd0 <br>MTD_open <br>MTD_write <br>MTD_close <br>/var/tmp&gt; <br>同样可以使用/dev/mtd1，那么在下面的Mount中要使用mtdblock1。 <br><br>10．Mount/umount JFFS2分区 <br><br>10．1 Mount <br><br>在目标板上运行以下命令行： <br>/var/tmp&gt; mount -t jffs2 /dev/mtdblock0 /mnt <br>/var/tmp&gt;这样就mount上/mnt了。查看proc/mounts的内容： <br>/mnt&gt; cd /proc <br>/proc&gt; cat mounts <br>rootfs / rootfs rw 0 0 <br>/dev/root / romfs ro 0 0 <br>/proc /proc proc rw 0 0 <br>/dev/ram0 /var ext2 rw 0 0 <br>/dev/mtdblock0 /mnt jffs2 rw 0 0 <br>/proc&gt;我们还可以看看现在mnt下面的内容： <br>/var/tmp&gt; cd /mnt <br>/mnt&gt; ls <br>file1 <br>folder1 <br>folder2 <br>folder3 <br>/mnt&gt; cat file1 <br>Hello, this is JFFS2 on Nandflash. <br>/mnt&gt; <br>这样我们就可以在/mnt下操作Nandflash中的文件内容。例如： <br>/mnt&gt; rm file1 <br>/mnt&gt; vi file2 <br>This is ARMSYS board. <br>/mnt&gt; ls <br>file2 <br>folder1 <br>folder2 <br>folder3 <br>/mnt&gt; <br><br>10．2 umount <br><br>在开发板上运行以下命令行： <br>/&gt; umount /mnt <br>/&gt; cd mnt <br>/mnt&gt; ls <br>/mnt&gt; <br><br>11．JFFS2文件系统的使用 <br><br>由于Nandflash是非易失性的存储器，因此保存在其中的内容不会随掉电而消失。因此，在第一次成功完成了第9和第10步骤之后，以后只要直接运行第10步的mount即可。我们可以试试看。关闭开发板电源，再重新打开，同样下载和启动uClinux。启动后直接输入如下的命令行： <br>/&gt; mount -t jffs2 /dev/mtdblock0 /mnt <br>/&gt; cd /mnt <br>/mnt&gt; ls <br>file2 <br>folder1 <br>folder2 <br>folder3 <br>/mnt&gt; <br>我们看到Nandflash中的第1分区还是保存着上次操作的内容。如果你希望再每次uClinux启动时，自动将Nandflash的第1分区mount到/mnt目录，可以在/vendors/Samsung/44B0目录下的rc文件中加入1行即可： <br>mount &#8211;t jffs2 /dev/mtdblock0 /mnt <br><br>请通过发邮件到<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#115;&#117;&#112;&#112;&#111;&#114;&#116;&#64;&#104;&#122;&#108;&#105;&#116;&#97;&#105;&#46;&#99;&#111;&#109;&#46;&#99;&#110;"><font color=#000000>support@hzlitai.com.cn</font></a>来同作者交流或指出这篇文档的问题。</div>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/36885.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-11-26 20:58 <a href="http://www.cnitblog.com/zouzheng/articles/36885.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>S3C2410平台上支持NAND boot的u-boot移植和共享</title><link>http://www.cnitblog.com/zouzheng/articles/36884.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Mon, 26 Nov 2007 12:57:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/36884.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/36884.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/36884.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/36884.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/36884.html</trackback:ping><description><![CDATA[<h4>S3C2410平台上支持NAND boot的u-boot移植和共享</h4>
<div class=tpc_content>　　小弟现有一个可用的nand flash启动的u-boot源代码愿意分享，具体下载地址如下： <br>　　<a href="http://www.hhcn.org/maindoc/nandboot-2410-luofuchong.rar"><a href="http://www.hhcn.org/maindoc/nandboot-2410-luofuchong.rar" target=_blank><font color=#000000>http://www.hhcn.org/maindoc/nandboot-2410-luofuchong.rar</font></a></a> 在此感谢华恒斑竹hn的帮助和提供空间^_^。<br>　　小弟不敢贪功，先注明一下这个源码的出处： 补丁来源于以下网站： <br>　　<a href="http://linux.insigma.com.cn/devbbs/dispbbs.asp?boardID=15&amp;ID=382&amp;page=1" target=_blank><a href="http://linux.insigma.com.cn/devbbs/dispbbs.asp?boardID=15&amp;ID=382&amp;page=1" target=_blank><font color=#000000>http://linux.insigma.com.cn/devbbs/dispbbs.asp?boardID=15&amp;ID=382&amp;page=1</font></a></a><br>　　其实这个补丁早就下载了，但小弟不懂cvs的用法，怕麻烦一直没下载其对应版本的源码（在这里的显示版本虽然是1.1.4，但可是经过多人修改的，所以和官方的版本相差甚远）。 <br>　　后来我哥把它下载下来，于是就抽空把它编译了一下，串口居然有输出，兴致来了，花了几天搞了一下，现在基本可以用了。<br><br>这是cvs下载源码的方法： <br>cvs -d:pserver:anonymous@u-boot.cvs.sourceforge.net:/cvsroot/u-boot login <br>cvs -z3 -d:pserver:anonymous@u-boot.cvs.sourceforge.net:/cvsroot/u-boot co -D 20060523 -P u-boot <br>我经已经试过可以下载的了，不过最好晚上再下载成功率会高一点^_^ <br>登录的时候提示密码的时候直接按回车就行。<br><br><span class=text_title>在些说一下这几天编译的经验：</span> <br>1）打上补丁的过程中提示2、3个文件有些地方未能正常打补丁，但不影响编译。 <br>2）要用2.95.3的交叉编译器来编译，我试过用3.3.2的编译器来编译通过，但nand flash检测的那段代码没有运行，导致nand flash检测不出来，这可花了我不少的时间~_~ <br>3）附上的补丁是我修改过的（主要是一些提示信息和env地址的修改），打补丁的方法如下（假如你把补丁放到u-boot目录下）： <br><br>代码：<br>---------------------------------------------------------------------------------------------<br>patch -p1 &lt;u-boot-lfc.patch <br>---------------------------------------------------------------------------------------------<br>好了，下面介绍一下这个u-boot的用法： <br><span class=text_title>U-Boot 1.1.4 (Oct 23 2006 - 22:04:37)</span><br><br>U-Boot code: 33F80000 -&gt; 33F9DC98 BSS: -&gt; 33FA2308 <br>RAM Configuration: <br>Bank #0: 30000000 64 MB <br>Nor Flash: 512 kB <br>Nand Flash: 64 MB <br><br>In: serial <br>Out: serial <br>Err: serial <br>Hit any key to stop autoboot: 0<br><br>sbc2410=&gt; printenv <br>bootargs=noinitrd root=/dev/mtdblock/3 console=ttySAC0 <br>bootdelay=3 <br>baudrate=115200 <br>ethaddr=08:00:3e:26:0a:5b <br>bootfile="zImage" <br>filesize=13469c <br>fileaddr=30008000 <br>netmask=255.255.255.0 <br>ipaddr=192.168.1.128 <br>serverip=192.168.1.15 <br>bootcmd=nand read 30008000 30000 1d0000;go 30008000 <br>stdin=serial <br>stdout=serial <br>stderr=serial <br>Environment size: 319/65532 bytes<br><br>sbc2410=&gt; nand <br>nand - NAND sub-system <br>info - show available NAND devices <br>nand device [dev] - show or set current device <br>nand read[.jffs2[s] addr off size <br>nand write[.jffs2] addr off size - read/write `size' bytes starting at offset `off' to/from memory address `addr' <br>nand erase [clean] [off size] - erase `size' bytes from offset `off' (entire device if not specified) <br>nand bad - show bad blocks <br>nand read.oob addr off size - read out-of-band data <br>nand write.oob addr off size - read out-of-band data<br><br>注：这是对nand flash操作的命令，我修改过源码好让它提示出来（默认好像没有提示）<br>按照提示应该都会它的用法了吧？不过在此还是举一个烧写内核的例子吧：<br>sbc2410=&gt; tftp <br>TFTP from server 192.168.1.15; our IP address is 192.168.1.128 <br>Filename 'zImage'. <br>Load address: 0x30008000 <br>Loading: ################################################################# <br>################################################################# <br>################################################################# <br>#################################################### <br>done <br>Bytes transferred = 1263260 (13469c hex) <br>注：可以看出，网络也是好的^_^ <br><br>sbc2410=&gt; nand erase 30000 1d0000 <br>NAND erase: device 0 offset 196608, size 1900544 ... OK<br><br>sbc2410=&gt; nand write 30008000 30000 1d0000 <br>NAND write: device 0 offset 196608, size 1900544 ... 1900544 bytes written: OK <br><br>OK，现在内核已经写入nand flash了，想开机引导它？<br><br>sbc2410=&gt; setenv bootcmd nand read 30008000 30000 1d0000\;go 30008000 <br>sbc2410=&gt; saveenv <br>Saving Environment to NAND... <br>Erasing Nand...Writing to Nand... done<br><br>这样就可以了。好了，现在按一下你的复位键试试： <br>U-Boot 1.1.4 (Oct 23 2006 - 21:28:24) <br><br>U-Boot code: 33F80000 -&gt; 33F9DC88 BSS: -&gt; 33FA22F8 <br>RAM Configuration: <br>Bank #0: 30000000 64 MB <br>Nor Flash: 512 kB <br>Nand Flash: 64 MB<br><br>In: serial <br>Out: serial <br>Err: serial <br>Hit any key to stop autoboot: 0<br><br>NAND read: device 0 offset 196608, size 1900544 ... 1900544 bytes read: OK <br>## Starting application at 0x30008000 ... <br>Uncompressing Linux................................................................................. done, booting the kernel. <br>Linux version 2.6.14 (root@luofuchong) (gcc version 3.4.1) #21 Fri Oct 20 17:20:39 CST 2006 <br>CPU: ARM920Tid(wb) [41129200] revision 0 (ARMv4T) <br>Machine: SMDK2410 <br>ATAG_INITRD is deprecated; please update your bootloader. <br>Memory policy: ECC disabled, Data cache writeback <br>CPU S3C2410A (id 0x32410002) <br>S3C2410: core 202.800 MHz, memory 101.400 MHz, peripheral 50.700 MHz <br>S3C2410 Clocks, (c) 2004 Simtec Electronics <br>CLOCK: Slow mode (1.500 MHz), fast, MPLL on, UPLL on <br>USB Control, (c) 2006 sbc2410 <br>CPU0: D VIVT write-back cache <br>CPU0: I cache: 16384 bytes, associativity 64, 32 byte lines, 8 sets <br>CPU0: D cache: 16384 bytes, associativity 64, 32 byte lines, 8 sets <br>Built 1 zonelists <br>Kernel command line: noinitrd root=/dev/mtdblock/3 console=ttySAC0 <br>irq: clearing subpending status 00000002 <br>PID hash table entries: 512 (order: 9, 8192 bytes) <br>timer tcon=00500000, tcnt a509, tcfg 00000200,00000000, usec 00001e4c <br>Console: colour dummy device 80x30 <br>Dentry cache hash table entries: 16384 (order: 4, 65536 bytes) <br>Inode-cache hash table entries: 8192 (order: 3, 32768 bytes) <br>Memory: 64MB = 64MB total <br>Memory: 62208KB available (1924K code, 529K data, 108K init) <br>Mount-cache hash table entries: 512 <br>CPU: Testing write buffer coherency: ok <br>softlockup thread 0 started up. <br>NET: Registered protocol family 16 <br>S3C2410: Initialising architecture <br>SCSI subsystem initialized <br>usbcore: registered new driver usbfs <br>usbcore: registered new driver hub <br>S3C2410 DMA Driver, (c) 2003-2004 Simtec Electronics <br>DMA channel 0 at c4800000, irq 33 <br>DMA channel 1 at c4800040, irq 34 <br>DMA channel 2 at c4800080, irq 35 <br>DMA channel 3 at c48000c0, irq 36 <br>NetWinder Floating Point Emulator V0.97 (double precision) <br>devfs: 2004-01-31 Richard Gooch (rgooch@atnf.csiro.au) <br>devfs: devfs_debug: 0x0 <br>devfs: boot_options: 0x1 <br>yaffs Oct 18 2006 12:39:51 Installing. <br>Console: switching to colour frame buffer device 30x40 <br>fb0: s3c2410fb frame buffer device <br>fb1: Virtual frame buffer device, using 1024K of video memory <br>led driver initialized <br>s3c2410 buttons successfully loaded <br>s3c2410_serial0 at MMIO 0x50000000 (irq = 70) is a S3C2410 <br>s3c2410_serial1 at MMIO 0x50004000 (irq = 73) is a S3C2410 <br>s3c2410_serial2 at MMIO 0x50008000 (irq = 76) is a S3C2410 <br>io scheduler noop registered <br>io scheduler anticipatory registered <br>io scheduler deadline registered <br>io scheduler cfq registered <br>RAMDISK driver initialized: 16 RAM disks of 4096K size 1024 blocksize <br>usbcore: registered new driver ub <br>Cirrus Logic CS8900A driver for Linux (Modified for SMDK2410) <br>eth0: CS8900A rev E at 0xe0000300 irq=53, no eeprom , addr: 08: 0:3E:26:0A:5B <br>S3C24XX NAND Driver, (c) 2004 Simtec Electronics <br>s3c2410-nand: mapped registers at c4980000 <br>s3c2410-nand: timing: Tacls 10ns, Twrph0 30ns, Twrph1 10ns <br>NAND device: Manufacturer ID: 0xec, Chip ID: 0x76 (Samsung NAND 64MiB 3,3V 8-bit) <br>Scanning device for bad blocks <br>Bad eraseblock 1884 at 0x01d70000 <br>Creating 4 MTD partitions on "NAND 64MiB 3,3V 8-bit": <br>0x00000000-0x00020000 : "vivi" <br>0x00020000-0x00030000 : "param" <br>0x00030000-0x00200000 : "kernel" <br>0x00200000-0x04000000 : "root" <br>usbmon: debugfs is not available <br>s3c2410-ohci s3c2410-ohci: S3C24XX OHCI <br>s3c2410-ohci s3c2410-ohci: new USB bus registered, assigned bus number 1 <br>s3c2410-ohci s3c2410-ohci: irq 42, io mem 0x49000000 <br>hub 1-0:1.0: USB hub found <br>hub 1-0:1.0: 2 ports detected <br>Initializing USB Mass Storage driver... <br>usbcore: registered new driver usb-storage <br>USB Mass Storage support registered. <br>usbcore: registered new driver usbmouse <br>drivers/usb/input/usbmouse.c: v1.6:USB HID Boot Protocol mouse driver <br>mice: PS/2 mouse device common for all mice <br>s3c2410 TouchScreen successfully loaded <br>UDA1341 audio driver initialized <br>NET: Registered protocol family 2 <br>IP route cache hash table entries: 1024 (order: 0, 4096 bytes) <br>TCP established hash table entries: 4096 (order: 2, 16384 bytes) <br>TCP bind hash table entries: 4096 (order: 2, 16384 bytes) <br>TCP: Hash tables configured (established 4096 bind 4096) <br>TCP reno registered <br>TCP bic registered <br>NET: Registered protocol family 1 <br>Root-NFS: No NFS server available, giving up. <br>VFS: Unable to mount root fs via NFS, trying floppy. <br>yaffs: dev is 32505859 name is "mtdblock3" <br>yaffs: Attempting MTD mount on 31.3, "mtdblock3" <br>block 1757 is bad <br>VFS: Mounted root (yaffs filesystem). <br>Mounted devfs on /dev <br>Freeing init memory: 108K<br><br>init started: BusyBox v1.1.3 (2006.09.20-14:52+0000) multi-call binary <br>Starting pid 695, console /dev/tts/0: '/etc/init.d/rcS' <br>Please press Enter to activate this console. <br>注：这就是在我的板上的启动信息了^_^ <br><br><span class=text_title>遗留的问题： </span><br>1：这个u-boot只提供jffs2文件系统的烧写功能（具体未用过，因为我只搞过yaffs文件系统~_~），没有对yaffs文件系统的烧写支持，我想这部分就留给各位去做吧^_^ <br>2：好像用同样的方法来启动2.4内核解压后内核不能启动，但2.6内核就可以，具体不知道原因，知道的朋友请告知并把修改好的补丁共享，发挥一下共享精神，谢谢！ <br></div>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/36884.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-11-26 20:57 <a href="http://www.cnitblog.com/zouzheng/articles/36884.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>嵌入式相关的网站</title><link>http://www.cnitblog.com/zouzheng/articles/33232.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Tue, 11 Sep 2007 06:00:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/33232.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/33232.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/33232.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/33232.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/33232.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 嵌入式相关的技术网站&nbsp;&nbsp;<a href='http://www.cnitblog.com/zouzheng/articles/33232.html'>阅读全文</a><img src ="http://www.cnitblog.com/zouzheng/aggbug/33232.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-09-11 14:00 <a href="http://www.cnitblog.com/zouzheng/articles/33232.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>U-Boot在S3C2410上的移植</title><link>http://www.cnitblog.com/zouzheng/articles/25509.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Mon, 09 Apr 2007 16:54:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/25509.html</guid><description><![CDATA[<strong>引言 <br><br></strong>　　U-Boot是用于初始化目标板硬件，为嵌入式操作系统提供目标板硬件配置信息，完成嵌入
式操作系统装载、引导和运行的固件程序。它能够将系统的软硬件紧密衔接在一起。S3C2410是三星公司的一款基于ARM920T核的嵌入式通用处理器。
本文将详细介绍U-Boot在S3C2410开发板上的移植与运行。 <br><br><strong>　　U-BOOT简介</strong>&nbsp; <br><br>
U-Boot支持ARM、&nbsp;PowerPC等多种架构的处理器，也支持Linux、NetBSD和VxWorks等多种操作系统。它提供启动加载和下载
两种工作模式。启动加载模式也称自主模式，一般是将存储在目标板Flash中的内核和文件系统的镜像装载到SDRAM中，整个过程无需用户的介入。在使用
嵌入式产品时，一般工作在该模式下。工作在下载模式时，目标板往往受外设(一般是PC机)的控制，从而将外设中调试好的内核和文件系统下载到目标板中去。
U-Boot允许用户在这两种工作模式间进行切换。通常目标板启动时，会延时等待一段时间，如果在设定的延时时间范围内，用户没有按键，U-Boot就进
入启动加载模式。 <br><br>　　开发板的主要配置包括三星ARM9处理器S3C2410、1个串口和JTAG接口，晶振为12MHz，系统主频
为200MHz。另外，开发板上还包括1片4M&#215;16位数据宽度的Flash，地址范围为0x01000000～0x01800000和2片8M&#215;16位
数据宽度的SDRAM，地址范围为0x30000000～0x32000000。Flash使用了2410处理器的BANK0单元，由于2410中地址是
循环映射的，因而0x01000000&nbsp;和0x0地址等同。 <br><br>　　在本系统中，U-Boot的主要功能包括：建立和初始化RAM；初始化一个串口；检测机器的体系结构，传递MACH_TYPE_xxx的值(SMDK2410)给内核；建立内核的标记列表(tagged&nbsp;list)；调用内核镜像。 <br><br><strong>　　U-Boot移植步骤 <br><br></strong>　　为了使U-Boot支持新的开发板，一种简便的做法是在U-Boot已经支持的开发板中选择一种和目标板接近的，并在其基础上进行修改。代码修改的步骤如下：&nbsp;<br>　　1)在board目录下创建smdk2410目录，添加smdk2410.c、flash.c、memsetup.s、u-boot.lds和config.mk等； <br>　　2)在cpu目录下创建arm920t目录，主要包含start.s、interrupts.c、cpu.c、serial.c和speed.c等文件； <br>　　3)在include/configs目录下添加smdk2410.h，它定义了全局的宏定义等； <br>　　4)修改u-boot根目录下的Makefile文件： <br>　　smdk2410_config&nbsp;:&nbsp;unconfig@./mkconfig&nbsp;$(@:_config=)&nbsp;arm&nbsp;arm920t&nbsp;smdk2410 <br>　　5)运行make&nbsp;smdk2410_config，如果没有错误，就可以开始进行与硬件相关的代码移植工作。由于这部分代码与硬件紧密相关，所以要熟悉开发板的硬件配置，可参考各芯片的用户手册。 <br>　　　　　 <br><strong>　　U-Boot启动过程&nbsp; <br></strong><br>
U-Boot的启动过程可以分成3个阶段。首先在Flash中运行汇编程序，将Flash中的启动代码部分复制到SDRAM中，同时创造环境准备运行C
程序；然后在SDRAM中执行，对硬件进行初始化；最后设置内核参数的标记列表，复制镜像文件，进入内核的入口函数。&nbsp;<br><br>　　1)&nbsp;程序首
先在Flash中运行CPU入口函数/cpu/arm920t/start.s。具体工作包括：设置异常的入口地址和异常处理函数；配置PLLCON寄存
器，确定系统的主频；屏蔽看门狗和中断；初始化I/O寄存器；关闭MMU功能；调用/board/smdk2410中的memsetup.s，初始化存储
器空间，设置刷新频率；将U-Boot的内容复制到SDRAM中；设置堆栈的大小，ldr&nbsp;pc,&nbsp;_start_armboot。 <br><br>　　board/s3c2410中config.mk文件(TEXT_BASE&nbsp;=&nbsp;0x31F00000)用于设置程序编译连接的起始地址，在程序中要特别注意与地址相关指令的使用。 <br><br>　　当程序在Flash中运行时，执行程序跳转时必须要使用跳转指令，而不能使用绝对地址的跳转(即直接对PC操作)。如果使用绝对地址，那么，程序的取指是相对于当前PC位置向前或者向后的32MB空间内，而不会跳入SDRAM中。 <br><br>　　2)&nbsp;程序跳转到SDRAM中执行/lib_arm/board.c中的start_armboot()函数。该函数将完成如下工作： <br>*设置通用端口rGPxCON;rGPxUP；设置处理器类型gd-&gt;bd-&gt;bi_arch_number&nbsp;=&nbsp;193；设置启动参数地址gd-&gt;bd-&gt;bi_boot_params&nbsp;=&nbsp;0x30000100； <br>*&nbsp;env_init：设置环境变量，初始化环境； <br>*&nbsp;init_baudrate：设置串口的波特率； <br>*&nbsp;serial_init：设置串口的工作方式； <br>*&nbsp;flash_init：设置ID号、每个分页的起始地址等信息，将信息送到相应的结构体中； <br>*&nbsp;dram_init：设置SDRAM的起始地址和大小； <br>*&nbsp;env_relocate：将环境变量的地址送到全局变量结构体中(gd-&gt;env_addr&nbsp;=&nbsp;(ulong)&amp;(env_ptr-&gt;data))； <br>*&nbsp;enable_interrupts：开启中断； <br>*&nbsp;main_loop：
该函数主要用于设置延时等待，从而确定目标板是进入下载操作模式还是装载镜像文件启动内核。在设定的延时时间范围内，目标板将在串口等待输入命令，当目标
板接到正确的命令后，系统进入下载模式。在延时时间到达后，如果没有接收到相关命令，系统将自动进入装载模式，执行
bootm&nbsp;30008000&nbsp;30800000命令，程序进入do_bootm_linux()函数，调用内核启动函数；&nbsp; <br><br>
3)&nbsp;装载模式下系统将执行do_bootm_linux()函数，0x30008000是内核在SDRAM中的起始地址；0x30800000是
ramdisk在SDRAM中的起始地址；0x40000是内核在Flash中的位置，0x100000是数据块的大小；0x140000是
ramdisk在FLASH中的位置，0x440000是数据块的大小。系统调用memcpy()函数将内核从flash和ramdisk复制到
SDRAM中，具体如下： <br>memcpy((void&nbsp;*)0x30008000,&nbsp;(void&nbsp;*)0x40000,&nbsp;0x100000)；//复制数据块 <br>memcpy((void&nbsp;*)0x30800000,&nbsp;(void&nbsp;*)0x140000,&nbsp;0x440000)；//复制数据块&nbsp;<br><br>　　通常，将内核参数传递给Linux操作系统有两种方法：采用struct&nbsp;param_struct结构体或标记列表。本系统中采用了第二种方法。 <br><br>
一个合法的标记列表开始于ATAG_CORE，结束于ATAG_NONE。ATAG_CORE可以为空，一个空的ATAG_CORE的size字段设为
&#8220;2&#8221;(0x00000002)。ATAG_NONE&nbsp;的size字段必须设为&#8220;0&#8221;。标记列表可以有任意多的标记(tag)。在嵌入式Linux系统
中，通常由U-Boot设置的启动参数有：ATAG_CORE、ATAG_MEM、ATAG_CMDLINE、ATAG_RAMDISK、
ATAG_INITRD等。 <br><br>　　在本系统中，传递参数时分别调用了以下tag： <br>setup_start_tag(bd)；&nbsp;&nbsp;&nbsp;//标记列表开始 <br>setup_memory_tags(bd);&nbsp;//设置内存的起始位置和大小 <br>setup_commandline_tag
(bd,&nbsp;commandline);&nbsp;/*Linux内核在启动时可以命令行参数的形式来接收信息，利用这一点可以向内核提供那些内核不能检测的硬件参
数信息，或者重载(override)内核检测到的信息，这里char&nbsp;*commandline&nbsp;"initrd=
0x30800000,0x440000&nbsp;&nbsp;root=/dev/ram&nbsp;init=/linuxrc&nbsp;console=ttyS0"；*/ <br>setup_ramdisk_tag(bd);&nbsp;&nbsp;//表示内核解压后ramdisk的大小 <br>setup_initrd_tag(bd,&nbsp;initrd_start,&nbsp;initrd_end);&nbsp;//设置ramdisk的大小和物理起始地址 <br>setup_end_tag(bd);&nbsp;&nbsp;&nbsp;&nbsp;//标记列表结束 <br><br>
其中bd_t&nbsp;*bd&nbsp;=&nbsp;gd-&gt;bd是指向bd_t&nbsp;结构体的指针，在该结构体中存放了关于开发板配置的基本信息。标记列表应该放在内核解压
和initrd的bootp程序都不会覆盖的内存区域，同时又不能和异常处理的入口地址相冲突。建议放在RAM起始的16K大小处,在本系统中即为
0x30000100处。 <br><br>　　U-BOOT调用&nbsp;Linux&nbsp;内核的方法是直接跳转到内核的第一条指令处，也即直接跳转到&nbsp;MEM_START＋0x8000地址处。在跳转时，要满足下列条件： <br>a)&nbsp;CPU寄存器的设置：R0＝0；R1＝机器类型&nbsp;ID，本系统的机器类型ID＝193。R2＝启动参数标记列表在RAM中的起始基地址；&nbsp; <br>b)&nbsp;CPU模式：必须禁止中断(IRQs和FIQs)；CPU必须工作在SVC模式； <br>c)&nbsp;Cache和MMU的设置：MMU&nbsp;必须关闭；指令Cache可以打开也可以关闭；数据Cache必须关闭。&nbsp; <br><br>　　系统采用下列代码来进入内核函数： <br>theKernel&nbsp;=&nbsp;(void&nbsp;(*)(int,&nbsp;int))ntohl(hdr-&gt;ih_ep); <br>theKernel
(0,&nbsp;bd-&gt;bi_arch_number);其中，hdr是image_header_t类型的结构体，hdr-&gt;ih_ep指向内核
的第一条指令地址，即Linux操作系统下的/kernel/arch/arm/boot/compressed/head.S汇编程序。
theKernel()函数调用应该不会返回，如果该调用返回，则说明出错。<br><br><strong>　　结语 <br></strong>　　本文总结介绍了U-Boot在S3C2410上的移植，移植完成后，U-Boot能够稳定地运行在开发板上，为后续的软件开发打下较好的基础． <br><br><strong>基于S3C2410处理器目标板的移植<br><br></strong>
<p>本文描述了将Linux移植到基于S3C2410处理器目标板上的方法与过程。介绍了目标平台和Linux，并说明了如何搭建移植环境，着重介绍了
Bootloader的架构和功能以及Linux内核的移植。结果证明该方法是可行的。移植后Linux在嵌入式系统中运行良好。 </p>
<p><strong>引言</strong> </p>
<p>嵌人式系统就是以应用为中心、以计算机技术为基础，软硬件可裁剪，适应应用系统对功能、可靠性、成本、体积、功耗等严格要求的专用计算机系统。
ARM嵌人式芯片是一种高性能、低功耗的RISC芯片。它由英国ARM公司设计,世界上几乎所有的主要半导体生产商都生产基于ARM体系机构的通用芯片，
或在其专用芯片中应用相关ARM技术。在2001年基于ARM内核的处理器占市场份额的75％，成为业界的龙头。 </p>
<p>Linux是免费发行的、快速高效的操作系统
，Linux的出现在计算机世界引发了一场革命。Linux操作系统以代码开放、功能强大又易于移植成为嵌入式操作新兴力量。嵌人式Linux是按照嵌人
式操作系统的要求设计的一种小型操作系统，由一个内核以及一些根据需要进行定制的系统模块组成。其内核很小
，一般只有几百kb，即使加上其他必要的模块和应用程序，所需的存储空间也很小。它非常适合于移植到嵌入式系统中去
，同时它具有多任务多进程的系统特征 ，有些还具有实时特征。 </p>
<p><strong>1 目标平台介绍</strong> </p>
<p>文中使用的目标平台S3C2410是SAMSUNG公司使用920T处理器内核开发的一款嵌入式处理器。S3C2410是Samsung公司专门为
PDA、Intenet设备和手持设备等专门开发的微处理器。该芯片还包含有16kB一体化的Cache/MMU，这一特性使开发人员能够将Linux和
VXwork移植到基于该处理器的目标系统中。该目标板的系统资源如下 ： </p>
<p>(1)CPU：S3C2410微处理器,工作频率为200MHz。 </p>
<p>(2)Flash：16MB。 </p>
<p>(3)SDRAM：64MB SDRAM。 </p>
<p>(4)UART：RS-232串行接口。 </p>
<p>(5)其他 ：14针ARM JTAG接口等。 </p>
<p><strong>2 交叉编译环境的建立</strong> </p>
<p>基于Linux操作系统的应用开发环境一般是由目标系统硬件(开发板)和宿主PC机所构成。目标硬件开发板用于运行操作系统和系统应用软件，而目标
板所用到的操作系统的内核编译、应用程序的开发和调试则需要通过宿主PC机来完成(所以称为交叉编译)。双方之间一般通过串口、并口或以太网接口建立连接
关系。单独拿出一台PC机，在该PC上安装桌面的Linux操作系统(如RedHatLinux9.0)，可以采用默认的安装选项(注意要包含FTP服
务)，这台PC作为Linux服务器，除管理员以外，一般不直接让其他人去操作。 </p>
<p>将该Linux服务器接入局域网，并新建一些合法用户，以便其他的PC机(在此将其称为工作站)的合法用户能访问到Linux服务器。而其他的PC机(工作站)仍然使用Windows操作系统。 </p>
<p>需要的软件工具包括 ： </p>
<p>(1)ftp客户端程序。 </p>
<p>(2)Telnet工具。 </p>
<p>(3)移植到某一特定ARM平台的Linux操作系统内核源码。 </p>
<p>(4)GNU编译工具，可由相关网站下载。 </p>
<p>在某工作站PC上安装ftp客户端程序和Telnet工具 ，安装完毕后应该可以在该工站PC和Linux服务器之间进行文件的传输，并在工作站PC可以通过Telnet登陆到Linux服务器(可能需要将Linux服务器 的防火墙服务关闭才能完成)。<br>  <br>Linux
服务器的安装：将工作站PC上的Linux操作系统内核源码压缩包和GNU编译工具通过ftp传送到Linux服务器的某个目录(如合法的用户目录)，然
后在该目录下解压，并将GNU编译工具安装到默认的工作目录即可，以上工作通过在工作站PC使用Telnet工具完成，而不需要在Linux服务器上进
行。Linux操作系统内核的编译一般有一个比较固定的步骤，会根据MakeFile文件的不同而略有差异，可参考相关文档，编译的工作在工作站PC使用
Telnet工具完成。按固定的步骤编译内核完成以后，会在相应目录生成可执行的二进制文件，通过ftp将该可执行的二进制文件传回工作站PC，然后通过
ADS或SDT下的烧写工具写入开发板的Flash即可。 </p>
<p><strong>3 Bootloader的架构和功能</strong> </p>
<p>Bootloader引导程序是嵌入式开发很重要的组成部分。它是嵌入式系统上电后执行的第一个程序，并由它最终将操作系统启动起来并将控制权交给
操作系统。Bootloader引导程序最基本的功能是对硬件系统的初始化和内核启动参数设置并启动内核。Bootloader的主要功能有： </p>
<p>(1)初始化CPU 的主频、SDRAM、中断、串口等硬件； </p>
<p>(2)启动Linux内核并提供一个RAMDISK； </p>
<p>(3)通过串口下载内核或RAMDISK到目标板上； </p>
<p>(4)将修改过的内核或RAMDISK写入到Flash内； </p>
<p>(5)为用户提供一个命令接口。 </p>
<p>在嵌入式系统开发过程中，Bootloader还与主机通信，不断检测从主机传来的控制信息和数据信息，完成相应的操作。 </p>
<p>Linux运行在保护模式下，但是当机器启动复位的时候却处于实模式下。所以写Bootloader的工作也是在实模式之下的。 </p>
<p>Botloader的实现除了依赖于CPU的体系结构，还依赖于目标板的设置，本系统的Bootloader引导程序分为stagel和
stage2两个阶段。依赖于CPU的体系机构的代码
，比如设备初始化等，放在stagel中，采用ARM汇编语言来实现，这样可以达到短小精悍的目的。stage2用C语言来实现，可以实现复杂的功能，同
时代码具有更好的可读性和可移植性。 </p>
<p>●Bootloader的stage1 </p>
<p>stage1是Bootloader一开始就执行的操作，其目的是为了stage2的执行以及随后的内核的执行，设置好一些基本的硬件环境。 </p>
<p>包括以下步骤 ： </p>
<p>①屏蔽所有的中断 。为中断提供服务的通常是操作系统,因此在执行Bootloader的过程中可以不响应任何中断。中断屏蔽通过写CPU的中断屏蔽寄存器来完成。 </p>
<p>②设置CPU的时钟频率和速度。 </p>
<p>③初始化RAM设置系统的内存控制器的功能寄存器和各内存库控制寄存器等。 </p>
<p>④为加载stage2准备RAM空间。 </p>
<p>⑤拷贝stage2到RAM中。 </p>
<p>⑥跳转到stage2的入口点。 </p>
<p>●Bootloader的stage2 </p>
<p>stage2的主要的功能是通过串口下载Linux内核到目标板上。 </p>
<p>包括以下几个步骤： </p>
<p>①初始化本阶段要使用到的硬件设备。这通常包括：初始化至少一个串口，以便和终端用户进行I/O输出信息；初始化计时器等。 </p>
<p>②检测系统的内存映射。所谓内存映射就是指在整个4GB物理地址空间中有哪些地址范围被分配用来寻址系统的RAM单元。 </p>
<p>③加载内核映像和根文件系统从Flash读入到Rom中。这里包括两个方面：a．内核映像所占用的内存范围；b．根文件系统所占用的内存范围。在规划内存占用的布局时，主要考虑基地址和映像的大小两个方面。 </p>
<p>④设置内核的启动参数。 </p>
<p>⑤调用内核。Bootloader调用Linux内核的方法是直接跳转到内核的第一条指令处。 </p>
<p> <br><strong>4 Linux内核的构建和移植</strong> </p>
<p>1)设置软件开发环境。 </p>
<p>为了和目标板进行通讯，必须要在主机上终端运行仿真程序DNW。建议在宿主机上安装REDHATLinux9.0版本。以下以在SHELL模式下安装编译器和源代码为例介绍安装过程 ： </p>
<p>首先以ROOT身份进入系统。把REDHATLinux90的安装光盘放入光驱中，执行装载光驱命令： </p>
<p><br>moumt/dev/cdrom /nmt/cdrom<br> </p>
<p>如果系统不能识别/mnt/cdrom，可以使用如下命令(假设cdrom为/dev/had)，则用mount -t iso9660 /dev/hdb/mnt。 </p>
<p>如果在安装REDHATLinux9.0的时候已经默认安装了CDROM，以上命令不需执行,直接进入CDROM所在目录。 </p>
<p><br>mkdir /usr/local/arm <br>mount /dev/cdrom /mnt/cdrom<br>cd/<br>cp /mnt/cdrom/bin/cross-2.95.3.tar.bz2 /usr/local/arm/<br>cp /mnt/cdrom/bin/Linux_for_s3c2410.gz<br>tar zxvf Linux_for_s3c2410.gz<br>cd /usr/local/arm/ <br>tar -jxvf cross-2.95.3.tar.bz2<br> </p>
<p>程序执行完后，Linux源代码和编译环境都已建立起来了。 </p>
<p>2)Linux内核编译。 </p>
<p>首先以ROOT身份进入系统。然后完成下列步骤，就可以在/Linux/arch/arm/boot/得到内核的映像文件zImage。 </p>
<p>在shell命令下输入命令进行内核配置： </p>
<p>make menuconfig </p>
<p>该命令执行之后生成文件.config。它保存这个配置信息,下一次再执行make menuconfig的时候将生成新的.config文件。 </p>
<p>输入命令 ：make dep </p>
<p>输入命令 ：make clean </p>
<p>输入命令 ：make zImage </p>
<p>通过各个目录的Makefile进行，将会在各个目录下生成一系列目标文件，上述步骤完成后，就完成了对Linux的编译工作。 </p>
<p>Linux内核的编译、修改、移植实际上与上层的应用程序，如命令解释器shell、登陆程序login相关。这些程序应根据需要重新定制。 </p>
<p>利用终端仿真程序DNW和Bootloader引导程序通过串口就可以把Linux内核移植到目标系统板。移植完成后，Linux就可以在目标系统板上运行了。 </p>
<p><strong>5 结束语</strong> </p>
讨论了Linux向目标系统S3C2410移植的过程中的关键技术及其实现。移植后的Linux保留了原有的工作稳定的特点。并可以根据需要，在该系统中编写应用程序和运行应用程序。 <br><br><br><span id="lblTitle">U-Boot在S3C2410上的移植分析<br><br></span><span id="lblContent" class="n12"><strong>摘　要</strong>：介绍了一款优秀的嵌入式BootLoader—U-Boot，详细讲解了它的运行原理，着重讨论了其在S3C2410上的移植过程，并对移植结果进行了测试。 <br><br><strong>关键词</strong>：U-Boot；S3C2410；移植；BootLoader&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>引　言</strong>&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;BootLoader
是嵌入式系统软件开发的第一个环节，它紧密地将软硬件衔接在一起，对于一个嵌入式设备后续的软件开发至关重要。BootLoader还涉及到许多硬件相关
的知识，对于普通的嵌入式开发板，它又是不可跳过的步骤，所以做好它的移植工作是必须的，对于后续的开发工作也是有益的。U-Boot是当前比较流行、功
能强大的BootLoader，它操作简便，可以支持多种体系结构的处理器，同时提供了完备的命令体系。
S3C2410是三星公司一款基于ARM920T的嵌入式通用处理器。本文的移植平台就是以S3C2410
为核心的HHARM9-EDU-R2开发板，这块开发板的硬件资源配置较为完善。主要硬件资源有：S3C2410处理器；1片Intel
TE28f128FLASH ( 16M)；2片Hynix HY57V561620
SDRAM(64M)；10/100M自适应网络芯片DM9000；
USBHost/Device；RS232&#215;2/RS485&#215;1串口；LQ035FLM08L
256K色TFT真彩LCD显示屏；全功能JTAG调试口等。&nbsp;<br><br><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;U-Boot简介</strong>&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;U
-Boot是Das U-Boot的简称，是由denx软件中心依照GPL
发布的公共软件，作为系统启动的引导模块，U-Boot支持多种处理器架构，比如Power-PC、ARM、MIPS和x86等。
目前，U-Boot源代码在sourceforge网站的社区服务器中，
Internet上有一群自由开发人员对其进行维护和开发，它的项目主页是http：//sourceforge.net/ projects/
u-boot。当下载并解压U-Boot
源码包后，会形成如下的目录结构：board，和一些已有开发板有关的文件；common，实现各种U-Boot 命令的C
文件；cpu，CPU相关文件，其中的子目录都是以U-Boot
所支持的CPU命名的；disk，disk驱动的分区处理代码；doc，文档；drivers，通用设备驱动程序；fs，支持文件系统的文件；
include，头文件，对各种硬件平台支持的汇编文件，系统的配置文件和对文件系统支持的文件；net：与网络有关的代码；
lib-arm，与ARM体系结构相关的代码；tools，创建S-Record 格式文件和U-Boot Images的工具。&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;本文中U-Boot的移植就是根据HHARM9-EDU-R2开发板的硬件资源在以上的目录中修改或者添加相关源文件，并且重新编译的过程。移植工作开始之前，了解U-Boot的运行过程是十分必要的。&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>U- Boot 运行过程分析</strong>&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;U
-Boot编译后的代码定义一般不超过100kB，并且这100 kB又分成两个阶段来执行。第一阶段的代码在start.s中定义，大小不超过10
kB，它包括从系统上电后在0x00000000
地址开始执行的部分。这部分代码运行在Flash中，它包括对S3C2410的一些寄存器的初始化和将U-Boot的第二阶段代码从Flash拷贝到
SDRAM中。除去第一阶段的代码，剩下的部分都是第二阶段的代码。
第二阶段的起始地址是在第一阶段代码中指定的，被复制到SDRAM后，就从第一阶段跳到这个入口地址开始执行剩余部分代码。
第二阶段主要是进行一些BSS 段设置，堆栈的初始化等工作，最后会跳转到main-loop函数中，接受命令并进行命令处理。图1
给出了U-Boot的详细的运行过程包括对内核的设置、装载及调用过程。&nbsp;<br><img  src="http://www.edires.net/sNewsSystem/Files/Images/3464.jpg" border="0"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;图1　U-Boot运行过程<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;了解了U-Boot 的运行过程以后，我们还必须确定开发板的地址空间分布，才可以进行源码的修改和移植工作。 地址空间的分布部分依赖于开发板的硬件配置及CPU 的复位地址。 本文中开发板的地址空间如图2 所示。&nbsp;<br><img  src="http://www.edires.net/sNewsSystem/Files/Images/3465.jpg" border="0"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;图2 &nbsp;开发板地址空间分布&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>U-Boot的移植与测试</strong>&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
为了使移植工作更加快捷，应当选择U-Boot当前发布的最新版本1.1.2
(尽管通过CVS可以得到U-Boot1.1.3，但其正在开发，尚未发布，不宜使用) 。
因为最新的版本可以提供尽可能多的处理器核及开发板的支持。 对于U-Boot-1.1.2 而言，它不仅提供对ARM-
920T内核的支持，而且直接提供了对于S3C2410 的板级支持，这使移植工作量相对减少。&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>支持ARM- 920T内核的代码修改</strong>&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;由于U-Boot-1.1.2 提供对ARM-920T 内核的直接支持，所以本步骤不需要做任何工作，本文为了让读者了解BootLoder 移植的通用模式，在此只是稍加提示。&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>配置自己的开发板</strong>&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>&nbsp;&nbsp;建立自己开发板的目录和相关文件</strong>。&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1)
在include/ configs目录中以smdk2410.h为模板添加头文件S3C2410. h(cp smdk2410.h
S3C2410.h) 。 这个文件是开发板的配置文件，它包括开发板的CPU、系统时钟、RAM、Flash系统及其它相关的配置信息。&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2)
在board/目录下创建S3C2410目录。拷贝smdk2410目录下所有文件到S3C2410目录下，共有如下六个文件：flash.c、
memsetup.c、S3C2410.c、Makefile、U-Boot .lds
和config.mk，根据开发板实际情况对各个文件进行修改。&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;◆flash.c.U-Boot读、写和删除Flash 设备的源代码文件。由于不同开发板中Flash 存储器的种类各不相同，所以，修改flash.c时需参考相应的Flash 芯片手册。它包括如下几个函数：&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unsigned long flash-init (void )，Flash初始化； <br>　　int flash-erase (flash-info-t *info，ints-first，ints -last)，Flash擦除； <br>　　volatile static int write- hword (flash-info-t *info，ulong&nbsp;dest ， ulong data) ，Flash 写入； <br>　　int write-buff (flash-info-t *info，uchar *src ，ulong&nbsp;addr，ulong cnt)，从内存复制数据。&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;由于本文开发板所用flash芯片为IntelTE28f128，在board/ cmi目录中有此flash.c，只需对其稍加修改即可使用。&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;◆memsetup.c.初始化时钟、SMC控制器和SDRAM控制器。为了以后能用U-Boot的GO命令执行修改过的用loadb或tftp下载的U-Boot.在标记符&#8220;0：&#8221;上加入五句： <br>　　&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov r3，pc <br>　　ldr r4，= 0x3FFF0000 <br>　　and r3，r3，r4 / /以上三句得到实际起动的内存地址 <br>　　aad r0，r0，r3 / /用GO 命令调试uboot时，启动地址在RAM <br>　　add r2，r2，r3 / /把初始化内存信息的地址，加上实际起动地址&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;◆S3C2410.C.设置各种总线时钟，打开数据Cache和指令Cache，并设置相关内存参数。&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;◆Makefile.修改：OBJS ：= S3C2410.o flash.omemsetup.o&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;◆U-Boot.lds.作如下修改：&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.text&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cpu/ arm920t/ start.o ( .text)&nbsp;<br>* (.text)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;◆config.mk.用于设置程序连接的起始地址，因为会在U-Boot 中增加功能，所以留下6M 的空间，修改33F80000 为33A00000。&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>实现网卡的驱动程序</strong>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在drivers/目录中以dm9000x.c和dm9000x.h为模板添加网口设备控制程序dm9000.c和dm9000.h，其中dm9000.c主要包括以下函数：&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int eth-init (bd-t *bd)，初始化网络设备；&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int eth-send(volatile void *，int)，发送数据包；&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int eth-rx(void)，接收数据包。&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void eth-halt (void)，关闭网络设备；&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;为了方便网络设备的数据读写操作，还定义了如下函数：&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static int dm9000-probe (void)，搜索DM9000芯片，分配空间并登记之；&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static u16 phy-read(int)，从Phyxcer寄存器读取一个字；&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static void phy-write ( int，u16)，写一个字到Phyxcer 寄存器；&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static u16 read-srom-word (int)，从SROM 读取一个字数据；&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static u8 DM9000-ior (int)，从I/ O 口读取一个字节；&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static void DM9000-iow(int reg，u8 value)，写一个字节到I/ O 口；&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;最后在drivers/Makefile中加入dm9000.o。&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;修改Makefile 文件&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在U-Boot-1.1.2/Makefile中ARM92xT Systems注释下面加入以下两行：&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;S3C2410-config ：unconfig <br>　　@./ mkconfig ＄( @：-config = ) arm arm920tS3C2410&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;其中&#8220;arm&#8221;是CPU的种类，arm920t是ARM CPU对应的代码目录，S3C2410是自己开发板对应的目录。&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;交叉编译器安装在：/ path/ armv4l-unknown -linux-目录下，所以把CROSS-COMPILE 设置成相应的路径： CROSS-COMPILE = / path/ arm4l-unknown-linux -&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;生成目标文件并进行测试&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;依次运行以下命令：&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# make clean&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# make S3C2410-config&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# make&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;之后会生成三个文件：&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;U-Boot ——ELF 格式的文件， 可以被大多数Debug 程序识别；&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;U-Boot.bin ——二进制文件，纯粹的U-Boot&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;二进制执行代码，不保存ELF 格式和调试信息。 这个文件一般用于烧录到用户开发板中；U-Boot .srec ——Motorola S-Record格式，可以通过串行口下载到开发板中。&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>测试与应用</strong>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1) 测试&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;利用编制好的Flash烧写程序，通过JTAG口将生成的二进制文件U-Boot。bin烧入Flash的零地址。 烧录成功后，拔掉JTAG调试线并复位开发板，从Minicom终端输出如下信息：&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;U-Boot 1.1.2 (Jul 20 2005-09 ：34 ：21)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;U-Boot code ： 33F00000-&gt; 33F1952C BSS：-&gt; 33F1D870&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RAM Configuration ：&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Bank # 0 ： 30000000 64 MB&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Flash Memory Start 0x0000000&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Device ID of the Flash is 18&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Flash ： 16 MB&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Write 18 to Watchdog and it is 18 now&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;In ： serial&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Out ： serial&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Err ： serial&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SMDK2410 #&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
串口输出的以上信息表明，CPU和串口已正常工作。通过U-Boot提供的命令flinfo和mtest可以测试Flash和RAM。经过测试，可以正确
地读出Flash信息及读写RAM，表明Flash 和DRAM 已正确初始化。
用tftp命令传输宿主机tftpboot目录下任一小文件到RAM成功，说明网卡芯片也成功驱动。&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2) 简单应用&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;U
-Boot的主要作用是用来引导内核。因此，通过U-Boot引导一个特定的内核，可以进一步测试其移植的稳定性。
而使用U-Boot引导内核有两种不同的方法。第一种方法是直接将内核映象文件和根文件系统烧写入Flash，使用此方法，U-Boot在启动时将
Flash中的内核映象及根文件系统读入RAM指定位置并从同一位置启动内核。
第二种方法是将内核映象文件和根文件系统下载至RAM中直接启动(而不是从Flash中读入RAM)
，此种方法不需要烧写Flash。笔者为了减少烧写Flash的次数，在本文中采用第二种方法，其步骤如下：&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SMDK2410 # tftp 30008000 zImage&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SMDK2410 # tftp 30800000 ramdisk.Image.gz&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SMDK2410 # go 30008000&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
上述指令执行的过程中，未出现异常，内核成功启动，并最终进入Shell提示符&#8220; #
&#8221;。在Shell提示符下输入内核编译时定制的各个命令，均可以正常运行。另外编写简单C程序，并用交叉编译器编译之，最终生成的可执行文件能够在开发板
上正常运行。上述事实说明内核经过U-Boot引导已稳定运行在开发板上。此次应用，进一步验证了U-Boot移植的稳定性。至此，移植工作告一段落。&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>结　语</strong>&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
目前，笔者移植的U-Boot已经能稳定地运行在开发板上，这使得Linux内核的调试脱离了BDM调试器，节约了大量的开发时间，大大提高了效率，是对
后续嵌入式开发的有力支持。
当然，U-Boot只是一款好用的BootLoader，嵌入式Linux的开发存在很多技术细节，只有根据实际情况不断修改、调试、总结，才能获得更大
的成功。 </span><br><img src ="http://www.cnitblog.com/zouzheng/aggbug/25509.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-04-10 00:54 <a href="http://www.cnitblog.com/zouzheng/articles/25509.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>内存管理内幕</title><link>http://www.cnitblog.com/zouzheng/articles/21742.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Wed, 10 Jan 2007 14:36:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/21742.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/21742.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/21742.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/21742.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/21742.html</trackback:ping><description><![CDATA[
		<p>
				<span class="atitle2">动态分配的选择、折衷和实现</span>
				<br />
		</p>
		<table cellspacing="0" cellpadding="0" border="0">
				<tbody>
						<tr valign="top" align="left">
								<td>
										<p>级别: 中级</p>
								</td>
						</tr>
				</tbody>
		</table>
		<p>
				<a href="http://www-128.ibm.com/developerworks/cn/linux/l-memory/#author1">
						<name />Jonathan Bartlett/A&gt;<br />技术总监, New Media Worx<br />2004 年 11 月 29 日</a>
		</p>
		<blockquote>
				<abstract-extended />本文将对 Linux™ 程序员可以使用的内存管理技术进行概述，虽然关注的重点是 C 语言，但同样也适用于其他语言。文中将为您提供如何管理内存的细节，然后将进一步展示如何手工管理内存，如何使用引用计数或者内存池来半手工地管理内存，以及如何使用垃圾收集自动管理内存。/BLOCKQUOTE&gt;
<p><a name="N1004C"><span class="atitle2">为什么必须管理内存</span></a><br />内存管理是计算机编程最为基本的领域之一。在很多脚本语言中，您不必担心内存是如何管理的，这并不能使得内存管理的重要性有一点点降低。对实际编程来说，理解您的内存管理器的能力与局限性至关重要。在大部分系统语言中，比如 C 和 C++，您必须进行内存管理。本文将介绍手工的、半手工的以及自动的内存管理实践的基本概念。 </p><p>追溯到在 Apple II 上进行汇编语言编程的时代，那时内存管理还不是个大问题。您实际上在运行整个系统。系统有多少内存，您就有多少内存。您甚至不必费心思去弄明白它有多少内存，因为每一台机器的内存数量都相同。所以，如果内存需要非常固定，那么您只需要选择一个内存范围并使用它即可。 </p><p>不过，即使是在这样一个简单的计算机中，您也会有问题，尤其是当您不知道程序的每个部分将需要多少内存时。如果您的空间有限，而内存需求是变化的，那么您需要一些方法来满足这些需求： </p><ul xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><li>确定您是否有足够的内存来处理数据。 
</li><li>从可用的内存中获取一部分内存。 
</li><li>向可用内存池（pool）中返回部分内存，以使其可以由程序的其他部分或者其他程序使用。 </li></ul><p></p><p>实现这些需求的程序库称为 <i xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">分配程序（allocators）</i>，因为它们负责分配和回收内存。程序的动态性越强，内存管理就越重要，您的内存分配程序的选择也就更重要。让我们来了解可用于内存管理的不同方法，它们的好处与不足，以及它们最适用的情形。 </p><p><a name="N1006E"><span class="atitle2">C 风格的内存分配程序</span></a><br />C 编程语言提供了两个函数来满足我们的三个需求： </p><ul xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><li><b>malloc：</b>该函数分配给定的字节数，并返回一个指向它们的指针。如果没有足够的可用内存，那么它返回一个空指针。 
</li><li><b>free：</b>该函数获得指向由 <code><font face="新宋体">malloc</font></code> 分配的内存片段的指针，并将其释放，以便以后的程序或操作系统使用（实际上，一些 <code><font face="新宋体">malloc</font></code> 实现只能将内存归还给程序，而无法将内存归还给操作系统）。 </li></ul><p></p><p><a name="N1008F"><span class="atitle3">物理内存和虚拟内存</span></a><br />要理解内存在程序中是如何分配的，首先需要理解如何将内存从操作系统分配给程序。计算机上的每一个进程都认为自己可以访问所有的物理内存。显然，由于同时在运行多个程序，所以每个进程不可能拥有全部内存。实际上，这些进程使用的是 <i xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">虚拟内存</i>。 </p><p>只是作为一个例子，让我们假定您的程序正在访问地址为 629 的内存。不过，虚拟内存系统不需要将其存储在位置为 629 的 RAM 中。实际上，它甚至可以不在 RAM 中 —— 如果物理 RAM 已经满了，它甚至可能已经被转移到硬盘上！由于这类地址不必反映内存所在的物理位置，所以它们被称为虚拟内存。操作系统维持着一个虚拟地址到物理地址的转换的表，以便计算机硬件可以正确地响应地址请求。并且，如果地址在硬盘上而不是在 RAM 中，那么操作系统将暂时停止您的进程，将其他内存转存到硬盘中，从硬盘上加载被请求的内存，然后再重新启动您的进程。这样，每个进程都获得了自己可以使用的地址空间，可以访问比您物理上安装的内存更多的内存。 </p><p>在 32-位 x86 系统上，每一个进程可以访问 4 GB 内存。现在，大部分人的系统上并没有 4 GB 内存，即使您将 swap 也算上， <i xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">每个进程</i>所使用的内存也肯定少于 4 GB。因此，当加载一个进程时，它会得到一个取决于某个称为 <i xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">系统中断点（system break）</i>的特定地址的初始内存分配。该地址之后是未被映射的内存 —— 用于在 RAM 或者硬盘中没有分配相应物理位置的内存。因此，如果一个进程运行超出了它初始分配的内存，那么它必须请求操作系统“映射进来（map in）”更多的内存。（映射是一个表示一一对应关系的数学术语 —— 当内存的虚拟地址有一个对应的物理地址来存储内存内容时，该内存将被映射。） </p><p>基于 UNIX 的系统有两个可映射到附加内存中的基本系统调用： </p><ul xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><li><b>brk：</b><code><font face="新宋体">brk()</font></code> 是一个非常简单的系统调用。还记得系统中断点吗？该位置是进程映射的内存边界。 <code><font face="新宋体">brk()</font></code> 只是简单地将这个位置向前或者向后移动，就可以向进程添加内存或者从进程取走内存。 
</li><li><b>mmap：</b><code><font face="新宋体">mmap()</font></code>，或者说是“内存映像”，类似于 <code><font face="新宋体">brk()</font></code>，但是更为灵活。首先，它可以映射任何位置的内存，而不单单只局限于进程。其次，它不仅可以将虚拟地址映射到物理的 RAM 或者 swap，它还可以将它们映射到文件和文件位置，这样，读写内存将对文件中的数据进行读写。不过，在这里，我们只关心 <code><font face="新宋体">mmap</font></code> 向进程添加被映射的内存的能力。 <code><font face="新宋体">munmap()</font></code> 所做的事情与 <code><font face="新宋体">mmap()</font></code> 相反。 </li></ul><p></p><p>如您所见， <code><font face="新宋体">brk()</font></code> 或者 <code><font face="新宋体">mmap()</font></code> 都可以用来向我们的进程添加额外的虚拟内存。在我们的例子中将使用 <code><font face="新宋体">brk()</font></code>，因为它更简单，更通用。 </p><p><a name="N100E4"><span class="atitle3">实现一个简单的分配程序</span></a><br />如果您曾经编写过很多 C 程序，那么您可能曾多次使用过 <code><font face="新宋体">malloc()</font></code> 和 <code><font face="新宋体">free()</font></code>。不过，您可能没有用一些时间去思考它们在您的操作系统中是如何实现的。本节将向您展示 <code><font face="新宋体">malloc</font></code> 和 <code><font face="新宋体">free</font></code> 的一个最简化实现的代码，来帮助说明管理内存时都涉及到了哪些事情。 </p><p>要试着运行这些示例，需要先 <a href="http://www-128.ibm.com/developerworks/cn/linux/l-memory/sidefile.html" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">复制本代码清单</a>，并将其粘贴到一个名为 malloc.c 的文件中。接下来，我将一次一个部分地对该清单进行解释。 </p><p>在大部分操作系统中，内存分配由以下两个简单的函数来处理： </p><ul xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><li><code><font face="新宋体">void *malloc(long numbytes)</font></code>：该函数负责分配 <code><font face="新宋体">numbytes</font></code> 大小的内存，并返回指向第一个字节的指针。 
</li><li><code><font face="新宋体">void free(void *firstbyte)</font></code>：如果给定一个由先前的 <code><font face="新宋体">malloc</font></code> 返回的指针，那么该函数会将分配的空间归还给进程的“空闲空间”。 </li></ul><p><code><font face="新宋体">malloc_init</font></code> 将是初始化内存分配程序的函数。它要完成以下三件事：将分配程序标识为已经初始化，找到系统中最后一个有效内存地址，然后建立起指向我们管理的内存的指针。这三个变量都是全局变量： </p><a name="N1012A"><b>清单 1. 我们的简单分配程序的全局变量</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>

        
int has_initialized = 0;

void *managed_memory_start;

void *last_valid_address;

      </code></pre></td></tr></tbody></table><p>如前所述，被映射的内存的边界（最后一个有效地址）常被称为系统中断点或者 <i xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">当前中断点</i>。在很多 UNIX® 系统中，为了指出当前系统中断点，必须使用 <code><font face="新宋体">sbrk(0)</font></code> 函数。 <code><font face="新宋体">sbrk</font></code> 根据参数中给出的字节数移动当前系统中断点，然后返回新的系统中断点。使用参数 <code><font face="新宋体">0</font></code> 只是返回当前中断点。这里是我们的 <code><font face="新宋体">malloc</font></code> 初始化代码，它将找到当前中断点并初始化我们的变量： </p><a name="N1014A"><b>清单 2. 分配程序初始化函数</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>

        
/* Include the sbrk function */

#include &lt;unistd.h&gt;

void malloc_init()

{

	/* grab the last valid address from the OS */

	last_valid_address = sbrk(0);


	/* we don't have any memory to manage yet, so
	 *just set the beginning to be last_valid_address
	 */

	managed_memory_start = last_valid_address;

	/* Okay, we're initialized and ready to go */

 	has_initialized = 1;

}

      </code></pre></td></tr></tbody></table><p>现在，为了完全地管理内存，我们需要能够追踪要分配和回收哪些内存。在对内存块进行了 <code><font face="新宋体">free</font></code> 调用之后，我们需要做的是诸如将它们标记为未被使用的等事情，并且，在调用 <code><font face="新宋体">malloc</font></code> 时，我们要能够定位未被使用的内存块。因此， <code><font face="新宋体">malloc</font></code> 返回的每块内存的起始处首先要有这个结构： </p><a name="N10163"><b>清单 3. 内存控制块结构定义</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>

        
struct mem_control_block {

	int is_available;

	int size;

};

      </code></pre></td></tr></tbody></table><p>现在，您可能会认为当程序调用 <code><font face="新宋体">malloc</font></code> 时这会引发问题 —— 它们如何知道这个结构？答案是它们不必知道；在返回指针之前，我们会将其移动到这个结构之后，把它隐藏起来。这使得返回的指针指向没有用于任何其他用途的内存。那样，从调用程序的角度来看，它们所得到的全部是空闲的、开放的内存。然后，当通过 <code><font face="新宋体">free()</font></code> 将该指针传递回来时，我们只需要倒退几个内存字节就可以再次找到这个结构。 </p><p>在讨论分配内存之前，我们将先讨论释放，因为它更简单。为了释放内存，我们必须要做的惟一一件事情就是，获得我们给出的指针，回退 <code><font face="新宋体">sizeof(struct mem_control_block)</font></code> 个字节，并将其标记为可用的。这里是对应的代码： </p><a name="N1017F"><b>清单 4. 解除分配函数</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>

        
void free(void *firstbyte) {

	struct mem_control_block *mcb;

	/* Backup from the given pointer to find the
	 * mem_control_block
	 */

	mcb = firstbyte - sizeof(struct mem_control_block);

	/* Mark the block as being available */

	mcb-&gt;is_available = 1;

	/* That's It!  We're done. */

	return;
}

      </code></pre></td></tr></tbody></table><p>如您所见，在这个分配程序中，内存的释放使用了一个非常简单的机制，在固定时间内完成内存释放。分配内存稍微困难一些。以下是该算法的略述： </p><a name="N1018C"><b>清单 5. 主分配程序的伪代码</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>

        

1. If our allocator has not been initialized, initialize it.

2. Add sizeof(struct mem_control_block) to the size requested.

3. start at managed_memory_start.

4. Are we at last_valid address?

5. If we are:

   A. We didn't find any existing space that was large enough
      -- ask the operating system for more and return that.

6. Otherwise:

   A. Is the current space available (check is_available from
      the mem_control_block)?

   B. If it is:

      i)   Is it large enough (check "size" from the
           mem_control_block)?

      ii)  If so:

           a. Mark it as unavailable

           b. Move past mem_control_block and return the
              pointer

      iii) Otherwise:

           a. Move forward "size" bytes

           b. Go back go step 4

   C. Otherwise:

      i)   Move forward "size" bytes

      ii)  Go back to step 4

      </code></pre></td></tr></tbody></table><p>我们主要使用连接的指针遍历内存来寻找开放的内存块。这里是代码： </p><a name="N10199"><b>清单 6. 主分配程序</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>

        
void *malloc(long numbytes) {

	/* Holds where we are looking in memory */

	void *current_location;

	/* This is the same as current_location, but cast to a
	 * memory_control_block
	 */

	struct mem_control_block *current_location_mcb;

	/* This is the memory location we will return.  It will
	 * be set to 0 until we find something suitable
	 */

	void *memory_location;

	/* Initialize if we haven't already done so */

	if(! has_initialized) 	{

		malloc_init();

	}

	/* The memory we search for has to include the memory
	 * control block, but the users of malloc don't need
	 * to know this, so we'll just add it in for them.
	 */

	numbytes = numbytes + sizeof(struct mem_control_block);

	/* Set memory_location to 0 until we find a suitable
	 * location
	 */

	memory_location = 0;

	/* Begin searching at the start of managed memory */

	current_location = managed_memory_start;

	/* Keep going until we have searched all allocated space */

	while(current_location != last_valid_address)

	{

		/* current_location and current_location_mcb point
		 * to the same address.  However, current_location_mcb
		 * is of the correct type, so we can use it as a struct.
		 * current_location is a void pointer so we can use it
		 * to calculate addresses.
		 */

		current_location_mcb =

			(struct mem_control_block *)current_location;

		if(current_location_mcb-&gt;is_available)

		{

			if(current_location_mcb-&gt;size &gt;= numbytes)

			{

				/* Woohoo!  We've found an open,
				 * appropriately-size location.
				 */

				/* It is no longer available */

				current_location_mcb-&gt;is_available = 0;

				/* We own it */

				memory_location = current_location;

				/* Leave the loop */

				break;

			}

		}

		/* If we made it here, it's because the Current memory
		 * block not suitable; move to the next one
		 */

		current_location = current_location +

			current_location_mcb-&gt;size;

	}

	/* If we still don't have a valid location, we'll
	 * have to ask the operating system for more memory
	 */

	if(! memory_location)

	{

		/* Move the program break numbytes further */

		sbrk(numbytes);

		/* The new memory will be where the last valid
		 * address left off
		 */

		memory_location = last_valid_address;

		/* We'll move the last valid address forward
		 * numbytes
		 */

		last_valid_address = last_valid_address + numbytes;

		/* We need to initialize the mem_control_block */

		current_location_mcb = memory_location;

		current_location_mcb-&gt;is_available = 0;

		current_location_mcb-&gt;size = numbytes;

	}

	/* Now, no matter what (well, except for error conditions),
	 * memory_location has the address of the memory, including
	 * the mem_control_block
	 */

	/* Move the pointer past the mem_control_block */

	memory_location = memory_location + sizeof(struct mem_control_block);

	/* Return the pointer */

	return memory_location;

 }

      </code></pre></td></tr></tbody></table><p>这就是我们的内存管理器。现在，我们只需要构建它，并在程序中使用它即可。 </p><p>运行下面的命令来构建 <code><font face="新宋体">malloc</font></code> 兼容的分配程序（实际上，我们忽略了 <code><font face="新宋体">realloc()</font></code> 等一些函数，不过， <code><font face="新宋体">malloc()</font></code> 和 <code><font face="新宋体">free()</font></code> 才是最主要的函数）： </p><a name="N101B9"><b>清单 7. 编译分配程序</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>


        
gcc -shared -fpic malloc.c -o malloc.so

      </code></pre></td></tr></tbody></table><p>该程序将生成一个名为 <i xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">malloc.so</i> 的文件，它是一个包含有我们的代码的共享库。 </p><p>在 UNIX 系统中，现在您可以用您的分配程序来取代系统的 <code><font face="新宋体">malloc()</font></code>，做法如下： </p><a name="N101D0"><b>清单 8. 替换您的标准的 malloc</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>

        
LD_PRELOAD=/path/to/malloc.so

export LD_PRELOAD

      </code></pre></td></tr></tbody></table><p><code><font face="新宋体">LD_PRELOAD</font></code> 环境变量使动态链接器在加载任何可执行程序之前，先加载给定的共享库的符号。它还为特定库中的符号赋予优先权。因此，从现在起，该会话中的任何应用程序都将使用我们的 <code><font face="新宋体">malloc()</font></code>，而不是只有系统的应用程序能够使用。有一些应用程序不使用 <code><font face="新宋体">malloc()</font></code>，不过它们是例外。其他使用 <code><font face="新宋体">realloc()</font></code> 等其他内存管理函数的应用程序，或者错误地假定 <code><font face="新宋体">malloc()</font></code> 内部行为的那些应用程序，很可能会崩溃。ash shell 似乎可以使用我们的新 <code><font face="新宋体">malloc()</font></code> 很好地工作。 </p><p>如果您想确保 <code><font face="新宋体">malloc()</font></code> 正在被使用，那么您应该通过向函数的入口点添加 <code><font face="新宋体">write()</font></code> 调用来进行测试。 </p><p>我们的内存管理器在很多方面都还存在欠缺，但它可以有效地展示内存管理需要做什么事情。它的某些缺点包括： </p><ul xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><li>由于它对系统中断点（一个全局变量）进行操作，所以它不能与其他分配程序或者 <code><font face="新宋体">mmap</font></code> 一起使用。 
</li><li>当分配内存时，在最坏的情形下，它将不得不遍历 <i>全部</i>进程内存；其中可能包括位于硬盘上的很多内存，这意味着操作系统将不得不花时间去向硬盘移入数据和从硬盘中移出数据。 
</li><li>没有很好的内存不足处理方案（ <code><font face="新宋体">malloc</font></code> 只假定内存分配是成功的）。 
</li><li>它没有实现很多其他的内存函数，比如 <code><font face="新宋体">realloc()</font></code>。 
</li><li>由于 <code><font face="新宋体">sbrk()</font></code> 可能会交回比我们请求的更多的内存，所以在堆（heap）的末端会遗漏一些内存。 
</li><li>虽然 <code><font face="新宋体">is_available</font></code> 标记只包含一位信息，但它要使用完整的 4-字节 的字。 
</li><li>分配程序不是线程安全的。 
</li><li>分配程序不能将空闲空间拼合为更大的内存块。 
</li><li>分配程序的过于简单的匹配算法会导致产生很多潜在的内存碎片。 
</li><li>我确信还有很多其他问题。这就是为什么它只是一个例子! </li></ul><p></p><p><a name="N10238"><span class="atitle3">其他 malloc 实现</span></a><br /><code><font face="新宋体">malloc()</font></code> 的实现有很多，这些实现各有优点与缺点。在设计一个分配程序时，要面临许多需要折衷的选择，其中包括： </p><ul xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><li>分配的速度。 
</li><li>回收的速度。 
</li><li>有线程的环境的行为。 
</li><li>内存将要被用光时的行为。 
</li><li>局部缓存。 
</li><li>簿记（Bookkeeping）内存开销。 
</li><li>虚拟内存环境中的行为。 
</li><li>小的或者大的对象。 
</li><li>实时保证。 </li></ul><p></p><p>每一个实现都有其自身的优缺点集合。在我们的简单的分配程序中，分配非常慢，而回收非常快。另外，由于它在使用虚拟内存系统方面较差，所以它最适于处理大的对象。 </p><p>还有其他许多分配程序可以使用。其中包括： </p><ul xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><li><b>Doug Lea Malloc：</b>Doug Lea Malloc 实际上是完整的一组分配程序，其中包括 Doug Lea 的原始分配程序，GNU libc 分配程序和 <code><font face="新宋体">ptmalloc</font></code>。 Doug Lea 的分配程序有着与我们的版本非常类似的基本结构，但是它加入了索引，这使得搜索速度更快，并且可以将多个没有被使用的块组合为一个大的块。它还支持缓存，以便更快地再次使用最近释放的内存。 <code><font face="新宋体">ptmalloc</font></code> 是 Doug Lea Malloc 的一个扩展版本，支持多线程。在本文后面的 <a href="http://www-128.ibm.com/developerworks/cn/linux/l-memory/#resources">参考资料</a>部分中，有一篇描述 Doug Lea 的 Malloc 实现的文章。 
</li><li><b>BSD Malloc：</b>BSD Malloc 是随 4.2 BSD 发行的实现，包含在 FreeBSD 之中，这个分配程序可以从预先确实大小的对象构成的池中分配对象。它有一些用于对象大小的 size 类，这些对象的大小为 2 的若干次幂减去某一常数。所以，如果您请求给定大小的一个对象，它就简单地分配一个与之匹配的 size 类。这样就提供了一个快速的实现，但是可能会浪费内存。在 <a href="http://www-128.ibm.com/developerworks/cn/linux/l-memory/#resources">参考资料</a>部分中，有一篇描述该实现的文章。 
</li><li><b>Hoard：编写 </b>Hoard 的目标是使内存分配在多线程环境中进行得非常快。因此，它的构造以锁的使用为中心，从而使所有进程不必等待分配内存。它可以显著地加快那些进行很多分配和回收的多线程进程的速度。在 <a href="http://www-128.ibm.com/developerworks/cn/linux/l-memory/#resources">参考资料</a>部分中，有一篇描述该实现的文章。 </li></ul><p></p><p>众多可用的分配程序中最有名的就是上述这些分配程序。如果您的程序有特别的分配需求，那么您可能更愿意编写一个定制的能匹配您的程序内存分配方式的分配程序。不过，如果不熟悉分配程序的设计，那么定制分配程序通常会带来比它们解决的问题更多的问题。要获得关于该主题的适当的介绍，请参阅 Donald Knuth 撰写的 <i xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">The Art of Computer Programming Volume 1: Fundamental Algorithms</i> 中的第 2.5 节“Dynamic Storage Allocation”（请参阅 <a href="http://www-128.ibm.com/developerworks/cn/linux/l-memory/#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</a>中的链接）。它有点过时，因为它没有考虑虚拟内存环境，不过大部分算法都是基于前面给出的函数。 </p><p>在 C++ 中，通过重载 <code><font face="新宋体">operator new()</font></code>，您可以以每个类或者每个模板为单位实现自己的分配程序。在 Andrei Alexandrescu 撰写的 <i xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Modern C++ Design</i> 的第 4 章（“Small Object Allocation”）中，描述了一个小对象分配程序（请参阅 <a href="http://www-128.ibm.com/developerworks/cn/linux/l-memory/#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</a>中的链接）。 </p><p><a name="N102AA"><span class="atitle3">基于 malloc() 的内存管理的缺点</span></a><br />不只是我们的内存管理器有缺点，基于 <code><font face="新宋体">malloc()</font></code> 的内存管理器仍然也有很多缺点，不管您使用的是哪个分配程序。对于那些需要保持长期存储的程序使用 <code><font face="新宋体">malloc()</font></code> 来管理内存可能会非常令人失望。如果您有大量的不固定的内存引用，经常难以知道它们何时被释放。生存期局限于当前函数的内存非常容易管理，但是对于生存期超出该范围的内存来说，管理内存则困难得多。而且，关于内存管理是由进行调用的程序还是由被调用的函数来负责这一问题，很多 API 都不是很明确。 </p><p>因为管理内存的问题，很多程序倾向于使用它们自己的内存管理规则。C++ 的异常处理使得这项任务更成问题。有时好像致力于管理内存分配和清理的代码比实际完成计算任务的代码还要多！因此，我们将研究内存管理的其他选择。 </p><p><a name="N102BE"><span class="atitle2">半自动内存管理策略</span></a><br /></p><p><a name="N102C5"><span class="atitle3">引用计数</span></a><br />引用计数是一种 <i xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">半自动（semi-automated）</i>的内存管理技术，这表示它需要一些编程支持，但是它不需要您确切知道某一对象何时不再被使用。引用计数机制为您完成内存管理任务。 </p><p>在引用计数中，所有共享的数据结构都有一个域来包含当前活动“引用”结构的次数。当向一个程序传递一个指向某个数据结构指针时，该程序会将引用计数增加 1。实质上，您是在告诉数据结构，它正在被存储在多少个位置上。然后，当您的进程完成对它的使用后，该程序就会将引用计数减少 1。结束这个动作之后，它还会检查计数是否已经减到零。如果是，那么它将释放内存。 </p><p>这样做的好处是，您不必追踪程序中某个给定的数据结构可能会遵循的每一条路径。每次对其局部的引用，都将导致计数的适当增加或减少。这样可以防止在使用数据结构时释放该结构。不过，当您使用某个采用引用计数的数据结构时，您必须记得运行引用计数函数。另外，内置函数和第三方的库不会知道或者可以使用您的引用计数机制。引用计数也难以处理发生循环引用的数据结构。 </p><p>要实现引用计数，您只需要两个函数 —— 一个增加引用计数，一个减少引用计数并当计数减少到零时释放内存。 </p><p>一个示例引用计数函数集可能看起来如下所示： </p><a name="N102E0"><b>清单 9. 基本的引用计数函数</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>

        
/* Structure Definitions*/

/* Base structure that holds a refcount */

struct refcountedstruct

{

	int refcount;

}

/* All refcounted structures must mirror struct
 * refcountedstruct for their first variables
 */

/* Refcount maintenance functions */

/* Increase reference count */

void REF(void *data)

{

	struct refcountedstruct *rstruct;

	rstruct = (struct refcountedstruct *) data;

	rstruct-&gt;refcount++;

}

/* Decrease reference count */

void UNREF(void *data)

{

	struct refcountedstruct *rstruct;

	rstruct = (struct refcountedstruct *) data;

	rstruct-&gt;refcount--;

	/* Free the structure if there are no more users */

	if(rstruct-&gt;refcount == 0)

	{

		free(rstruct);

	}

}

      </code></pre></td></tr></tbody></table><p><code><font face="新宋体">REF</font></code> 和 <code><font face="新宋体">UNREF</font></code> 可能会更复杂，这取决于您想要做的事情。例如，您可能想要为多线程程序增加锁，那么您可能想扩展 <code><font face="新宋体">refcountedstruct</font></code>，使它同样包含一个指向某个在释放内存之前要调用的函数的指针（类似于面向对象语言中的析构函数 —— 如果您的结构中包含这些指针，那么这是 <i xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">必需的</i>）。 </p><p>当使用 <code><font face="新宋体">REF</font></code> 和 <code><font face="新宋体">UNREF</font></code> 时，您需要遵守这些指针的分配规则： </p><ul xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><li><code><font face="新宋体">UNREF</font></code> 分配前左端指针（left-hand-side pointer）指向的值。 
</li><li><code><font face="新宋体">REF</font></code> 分配后左端指针（left-hand-side pointer）指向的值。 </li></ul><p></p><p>在传递使用引用计数的结构的函数中，函数需要遵循以下这些规则： </p><ul xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><li>在函数的起始处 REF 每一个指针。 
</li><li>在函数的结束处 UNREF 第一个指针。 </li></ul><p></p><p>以下是一个使用引用计数的生动的代码示例： </p><a name="N10328"><b>清单 10. 使用引用计数的示例</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>

        
/* EXAMPLES OF USAGE */


/* Data type to be refcounted */

struct mydata

{

	int refcount; /* same as refcountedstruct */

	int datafield1; /* Fields specific to this struct */

	int datafield2;

	/* other declarations would go here as appropriate */

};


/* Use the functions in code */

void dosomething(struct mydata *data)

{

	REF(data);

	/* Process data */

	/* when we are through */

	UNREF(data);

}


struct mydata *globalvar1;

/* Note that in this one, we don't decrease the
 * refcount since we are maintaining the reference
 * past the end of the function call through the
 * global variable
 */

void storesomething(struct mydata *data)

{

	REF(data); /* passed as a parameter */

	globalvar1 = data;

	REF(data); /* ref because of Assignment */

	UNREF(data); /* Function finished */

}

      </code></pre></td></tr></tbody></table><p>由于引用计数是如此简单，大部分程序员都自已去实现它，而不是使用库。不过，它们依赖于 <code><font face="新宋体">malloc</font></code> 和 <code><font face="新宋体">free</font></code> 等低层的分配程序来实际地分配和释放它们的内存。 </p><p>在 Perl 等高级语言中，进行内存管理时使用引用计数非常广泛。在这些语言中，引用计数由语言自动地处理，所以您根本不必担心它，除非要编写扩展模块。由于所有内容都必须进行引用计数，所以这会对速度产生一些影响，但它极大地提高了编程的安全性和方便性。以下是引用计数的益处： </p><ul xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><li>实现简单。 
</li><li>易于使用。 
</li><li>由于引用是数据结构的一部分，所以它有一个好的缓存位置。 </li></ul><p></p><p>不过，它也有其不足之处： </p><ul xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><li>要求您永远不要忘记调用引用计数函数。 
</li><li>无法释放作为循环数据结构的一部分的结构。 
</li><li>减缓几乎每一个指针的分配。 
</li><li>尽管所使用的对象采用了引用计数，但是当使用异常处理（比如 <code><font face="新宋体">try</font></code> 或 <code><font face="新宋体">setjmp()</font></code>/ <code><font face="新宋体">longjmp()</font></code>）时，您必须采取其他方法。 
</li><li>需要额外的内存来处理引用。 
</li><li>引用计数占用了结构中的第一个位置，在大部分机器中最快可以访问到的就是这个位置。 
</li><li>在多线程环境中更慢也更难以使用。 </li></ul><p></p><p>C++ 可以通过使用 <i xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">智能指针（smart pointers）</i>来容忍程序员所犯的一些错误，智能指针可以为您处理引用计数等指针处理细节。不过，如果不得不使用任何先前的不能处理智能指针的代码（比如对 C 库的联接），实际上，使用它们的后果通实比不使用它们更为困难和复杂。因此，它通常只是有益于纯 C++ 项目。如果您想使用智能指针，那么您实在应该去阅读 Alexandrescu 撰写的 <i xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Modern C++ Design</i> 一书中的“Smart Pointers”那一章。 </p><p><a name="N10379"><span class="atitle3">内存池</span></a><br />内存池是另一种半自动内存管理方法。内存池帮助某些程序进行自动内存管理，这些程序会经历一些特定的阶段，而且每个阶段中都有分配给进程的特定阶段的内存。例如，很多网络服务器进程都会分配很多针对每个连接的内存 —— 内存的最大生存期限为当前连接的存在期。Apache 使用了池式内存（pooled memory），将其连接拆分为各个阶段，每个阶段都有自己的内存池。在结束每个阶段时，会一次释放所有内存。 </p><p>在池式内存管理中，每次内存分配都会指定内存池，从中分配内存。每个内存池都有不同的生存期限。在 Apache 中，有一个持续时间为服务器存在期的内存池，还有一个持续时间为连接的存在期的内存池，以及一个持续时间为请求的存在期的池，另外还有其他一些内存池。因此，如果我的一系列函数不会生成比连接持续时间更长的数据，那么我就可以完全从连接池中分配内存，并知道在连接结束时，这些内存会被自动释放。另外，有一些实现允许注册 <i xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">清除函数（cleanup functions）</i>，在清除内存池之前，恰好可以调用它，来完成在内存被清理前需要完成的其他所有任务（类似于面向对象中的析构函数）。 </p><p>要在自己的程序中使用池，您既可以使用 GNU libc 的 obstack 实现，也可以使用 Apache 的 Apache Portable Runtime。GNU obstack 的好处在于，基于 GNU 的 Linux 发行版本中默认会包括它们。Apache Portable Runtime 的好处在于它有很多其他工具，可以处理编写多平台服务器软件所有方面的事情。要深入了解 GNU obstack 和 Apache 的池式内存实现，请参阅 <a href="http://www-128.ibm.com/developerworks/cn/linux/l-memory/#resources" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">参考资料</a>部分中指向这些实现的文档的链接。 </p><p>下面的假想代码列表展示了如何使用 obstack： </p><a name="N10395"><b>清单 11. obstack 的示例代码</b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>

        
#include &lt;obstack.h&gt;

#include &lt;stdlib.h&gt;

/* Example code listing for using obstacks */

/* Used for obstack macros (xmalloc is
   a malloc function that exits if memory
   is exhausted */

#define obstack_chunk_alloc xmalloc

#define obstack_chunk_free free

/* Pools */

/* Only permanent allocations should go in this pool */

struct obstack *global_pool;

/* This pool is for per-connection data */

struct obstack *connection_pool;

/* This pool is for per-request data */

struct obstack *request_pool;

void allocation_failed()

{

	exit(1);

}

int main()

{

	/* Initialize Pools */

	global_pool = (struct obstack *)

		xmalloc (sizeof (struct obstack));

	obstack_init(global_pool);

	connection_pool = (struct obstack *)

		xmalloc (sizeof (struct obstack));

	obstack_init(connection_pool);

	request_pool = (struct obstack *)

		xmalloc (sizeof (struct obstack));

	obstack_init(request_pool);

	/* Set the error handling function */

	obstack_alloc_failed_handler = &amp;allocation_failed;

	/* Server main loop */

	while(1)

	{

		wait_for_connection();

		/* We are in a connection */

		while(more_requests_available())

		{

			/* Handle request */

			handle_request();

			/* Free all of the memory allocated

			 * in the request pool

			 */

			obstack_free(request_pool, NULL);

		}

		/* We're finished with the connection, time

		 * to free that pool

		 */

		obstack_free(connection_pool, NULL);

	}

}

int handle_request()

{

	/* Be sure that all object allocations are allocated
	 * from the request pool
	 */

	int bytes_i_need = 400;

	void *data1 = obstack_alloc(request_pool, bytes_i_need);

	/* Do stuff to process the request */

	/* return */

	return 0;

}

      </code></pre></td></tr></tbody></table><p>基本上，在操作的每一个主要阶段结束之后，这个阶段的 obstack 会被释放。不过，要注意的是，如果一个过程需要分配持续时间比当前阶段更长的内存，那么它也可以使用更长期限的 obstack，比如连接或者全局内存。传递给 <code><font face="新宋体">obstack_free()</font></code> 的 <code><font face="新宋体">NULL</font></code> 指出它应该释放 obstack 的全部内容。可以用其他的值，但是它们通常不怎么实用。 </p><p>使用池式内存分配的益处如下所示： </p><ul xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><li>应用程序可以简单地管理内存。 
</li><li>内存分配和回收更快，因为每次都是在一个池中完成的。分配可以在 O(1) 时间内完成，释放内存池所需时间也差不多（实际上是 O(n) 时间，不过在大部分情况下会除以一个大的因数，使其变成 O(1)）。 
</li><li>可以预先分配错误处理池（Error-handling pools），以便程序在常规内存被耗尽时仍可以恢复。 
</li><li>有非常易于使用的标准实现。 </li></ul><p></p><p>池式内存的缺点是： </p><ul xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><li>内存池只适用于操作可以分阶段的程序。 
</li><li>内存池通常不能与第三方库很好地合作。 
</li><li>如果程序的结构发生变化，则不得不修改内存池，这可能会导致内存管理系统的重新设计。 
</li><li>您必须记住需要从哪个池进行分配。另外，如果在这里出错，就很难捕获该内存池。 </li></ul><p><a name="N103CB"><span class="atitle2">垃圾收集</span></a><br /><i xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">垃圾收集（Garbage collection）</i>是全自动地检测并移除不再使用的数据对象。垃圾收集器通常会在当可用内存减少到少于一个具体的阈值时运行。通常，它们以程序所知的可用的一组“基本”数据 —— 栈数据、全局变量、寄存器 —— 作为出发点。然后它们尝试去追踪通过这些数据连接到每一块数据。收集器找到的都是有用的数据；它没有找到的就是垃圾，可以被销毁并重新使用这些无用的数据。为了有效地管理内存，很多类型的垃圾收集器都需要知道数据结构内部指针的规划，所以，为了正确运行垃圾收集器，它们必须是语言本身的一部分。 </p><p><a name="N103D8"><span class="atitle3">收集器的类型</span></a><br /></p><ul xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><li><b>复制（copying）：</b> 这些收集器将内存存储器分为两部分，只允许数据驻留在其中一部分上。它们定时地从“基本”的元素开始将数据从一部分复制到另一部分。内存新近被占用的部分现在成为活动的，另一部分上的所有内容都认为是垃圾。另外，当进行这项复制操作时，所有指针都必须被更新为指向每个内存条目的新位置。因此，为使用这种垃圾收集方法，垃圾收集器必须与编程语言集成在一起。 
</li><li><b>标记并清理（Mark and sweep）：</b>每一块数据都被加上一个标签。不定期的，所有标签都被设置为 0，收集器从“基本”的元素开始遍历数据。当它遇到内存时，就将标签标记为 1。最后没有被标记为 1 的所有内容都认为是垃圾，以后分配内存时会重新使用它们。 
</li><li><b>增量的（Incremental）：</b>增量垃圾收集器不需要遍历全部数据对象。因为在收集期间的突然等待，也因为与访问所有当前数据相关的缓存问题（所有内容都不得不被页入（page-in）），遍历所有内存会引发问题。增量收集器避免了这些问题。 
</li><li><b>保守的（Conservative）：</b>保守的垃圾收集器在管理内存时不需要知道与数据结构相关的任何信息。它们只查看所有数据类型，并假定它们 <i>可以</i>全部都是指针。所以，如果一个字节序列可以是一个指向一块被分配的内存的指针，那么收集器就将其标记为正在被引用。有时没有被引用的内存会被收集，这样会引发问题，例如，如果一个整数域中包含一个值，该值是已分配内存的地址。不过，这种情况极少发生，而且它只会浪费少量内存。保守的收集器的优势是，它们可以与任何编程语言相集成。 </li></ul><p></p><p>Hans Boehm 的保守垃圾收集器是可用的最流行的垃圾收集器之一，因为它是免费的，而且既是保守的又是增量的，可以使用 <code><font face="新宋体">--enable-redirect-malloc</font></code> 选项来构建它，并且可以将它用作系统分配程序的简易替代者（drop-in replacement）（用 <code><font face="新宋体">malloc</font></code>/ <code><font face="新宋体">free</font></code> 代替它自己的 API）。实际上，如果这样做，您就可以使用与我们在示例分配程序中所使用的相同的 <code><font face="新宋体">LD_PRELOAD</font></code> 技巧，在系统上的几乎任何程序中启用垃圾收集。如果您怀疑某个程序正在泄漏内存，那么您可以使用这个垃圾收集器来控制进程。在早期，当 Mozilla 严重地泄漏内存时，很多人在其中使用了这项技术。这种垃圾收集器既可以在 Windows® 下运行，也可以在 UNIX 下运行。 </p><p>垃圾收集的一些优点： </p><ul xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><li>您永远不必担心内存的双重释放或者对象的生命周期。 
</li><li>使用某些收集器，您可以使用与常规分配相同的 API。 </li></ul><p></p><p>其缺点包括： </p><ul xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><li>使用大部分收集器时，您都无法干涉何时释放内存。 
</li><li>在多数情况下，垃圾收集比其他形式的内存管理更慢。 
</li><li>垃圾收集错误引发的缺陷难于调试。 
</li><li>如果您忘记将不再使用的指针设置为 null，那么仍然会有内存泄漏。 </li></ul><p></p><p><a name="N10430"><span class="atitle2">结束语</span></a><br />一切都需要折衷：性能、易用、易于实现、支持线程的能力等，这里只列出了其中的一些。为了满足项目的要求，有很多内存管理模式可以供您使用。每种模式都有大量的实现，各有其优缺点。对很多项目来说，使用编程环境默认的技术就足够了，不过，当您的项目有特殊的需要时，了解可用的选择将会有帮助。下表对比了本文中涉及的内存管理策略。 </p><p><a name="N1043A"><span class="atitle3">表 1. 内存分配策略的对比</span></a><br /></p><table cellspacing="1" cellpadding="3" width="60%" border="1"><tbody><tr valign="top"><td><b xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">策略</b></td><td><b xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">分配速度</b></td><td><b xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">回收速度</b></td><td><b xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">局部缓存</b></td><td><b xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">易用性</b></td><td><b xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">通用性</b></td><td><b xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">实时可用</b></td><td><b xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">SMP 线程友好</b></td></tr><tr><td>定制分配程序 </td><td>取决于实现 </td><td>取决于实现 </td><td>取决于实现 </td><td>很难 </td><td>无 </td><td>取决于实现 </td><td>取决于实现 </td></tr><tr><td>简单分配程序</td><td>内存使用少时较快</td><td>很快</td><td>差 </td><td>容易 </td><td>高 </td><td>否 </td><td>否 </td></tr><tr><td>GNU <code><font face="新宋体">malloc</font></code></td><td>中 </td><td>快 </td><td>中 </td><td>容易 </td><td>高 </td><td>否 </td><td>中</td></tr><tr><td>Hoard </td><td>中 </td><td>中 </td><td>中 </td><td>容易 </td><td>高 </td><td>否 </td><td>是 </td></tr><tr><td>引用计数 </td><td>N/A </td><td>N/A </td><td>非常好 </td><td>中 </td><td>中 </td><td>是（取决于 <code><font face="新宋体">malloc</font></code> 实现） </td><td>取决于实现 </td></tr><tr><td>池 </td><td>中 </td><td>非常快 </td><td>极好 </td><td>中 </td><td>中 </td><td>是（取决于 <code><font face="新宋体">malloc</font></code> 实现） </td><td>取决于实现 </td></tr><tr><td>垃圾收集 </td><td>中（进行收集时慢） </td><td>中 </td><td>差 </td><td>中 </td><td>中 </td><td>否 </td><td>几乎不 </td></tr><tr><td>增量垃圾收集 </td><td>中 </td><td>中 </td><td>中 </td><td>中 </td><td>中 </td><td>否 </td><td>几乎不 </td></tr><tr><td>增量保守垃圾收集 </td><td>中 </td><td>中 </td><td>中 </td><td>容易 </td><td>高 </td><td>否 </td><td>几乎不</td></tr></tbody></table></blockquote>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/21742.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-01-10 22:36 <a href="http://www.cnitblog.com/zouzheng/articles/21742.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ASCII 字符表</title><link>http://www.cnitblog.com/zouzheng/articles/21741.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Wed, 10 Jan 2007 14:31:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/21741.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/21741.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/21741.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/21741.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/21741.html</trackback:ping><description><![CDATA[
		<div class="postText">
				<p>数字代表计算机的语言。您的计算机如何使用字母来与程序和其他计算机进行通信？一种方法是把<a class="glossary" href="javascript:AppendPopup(this,'OfCharacterSet_1')"><font color="#000080">字符集<span class="AsstInlineDefText"><span class="ACICollapsed" id="divInlineDef_OfCharacterSet_1"> （字符集：一组共享一些关系的字母、数字和其他字符。例如，标准 ASCII 字符集包括字母、数字、符号和组成 ASCII 代码方案的控制代码。）</span></span></font></a>转换为数字形式。</p>
				<p>在 20 世纪 60 年代，标准化的需要带来了<a class="glossary" href="javascript:AppendPopup(this,'ofASCII_2')"><font color="#000080">美国标准信息交换码 (ASCII)<span class="AsstInlineDefText"><span class="ACICollapsed" id="divInlineDef_ofASCII_2" style="DISPLAY: none"> （ASCII：将英语中的字符表示为数字的代码。为每个字符分配一个介于 0 到 127 之间的数字。大多数计算机都使用 ASCII 表示文本和在计算机之间传输数据。）</span></span></font></a>（发音为 ask-kee）。ASCII 表包含 128 个数字，分配给了相应的<a class="glossary" href="javascript:AppendPopup(this,'ofCharacter_3')"><font color="#000080">字符<span class="AsstInlineDefText"><span class="ACICollapsed" id="divInlineDef_ofCharacter_3"> （字符：字母、数字、标点或符号。）</span></span></font></a>。ASCII 为计算机提供了一种存储数据和与其他计算机及程序交换数据的方式。</p>
				<p>ASCII 格式的文本不包含像黑体、斜体或字体等格式信息。当您使用 Microsoft 记事本或把文件作为纯文本保存在 Microsoft Office Word 中时，就会使用 ASCII。您可能读到过招聘广告，公司要求提供 ASCII 格式的简历。这意味着无论您是用电子邮件、传真或打印文本发送简历，公司希望您的简历中不含任何特殊格式。大公司可能用<a class="glossary" href="javascript:AppendPopup(this,'ofocr_4')"><font color="#000080">光学字符识别 (OCR)<span class="AsstInlineDefText"><span class="ACICollapsed" id="divInlineDef_ofocr_4"> （OCR：将文本图像（如扫描的文档）转换为实际的文本字符。也称为文本识别。）</span></span></font></a> 扫描软件来扫描简历和 ASCII 格式的文本。</p>
				<h2>在文档中插入 ASCII 字符</h2>
				<p>
						<strong>注释</strong>  该功能需要 Excel、FrontPage、InfoPath、OneNote、Outlook、PowerPoint、Project、Publisher、Word 或 Visio。</p>
				<p>除了在键盘上键入字符外，您也可以使用该符号的字符代码作为键盘快捷键。例如，要插入度数符号，在按住 ALT 的同时在数字键盘上键入 0176。</p>
				<ul>
						<li>要从下面的图表中插入 ASCII 字符，在按住 ALT 的同时键入等价的十进制数字。 
<p>例如，要插入度数符号，在按住 ALT 的同时在数字键盘上键入 0176。</p><p><strong>注释</strong>  必须使用数字键盘来键入数字，而不是键盘。如果您的键盘需要打开 Num Lock 键才能在数字键盘上键入数字，请务必打开它。</p></li>
				</ul>
				<h2>ASCII 打印字符</h2>
				<p>数字 32–126 分配给了能在键盘上找到的字符，当您查看或打印文档时就会出现。数字 127 代表 DELETE 命令。</p>
				<p>
						<a class="DropDown" href="javascript:ToggleDiv('divExpCollAsst_1')">
								<img id="divExpCollAsst_1_img" alt="隐藏" src="http://office.microsoft.com/global/images/bluedrop.gif" border="0" />
								<font color="#000080">ASCII 打印字符表</font>
						</a>
				</p>
				<span class="ACECollapsed" id="divExpCollAsst_1" style="DISPLAY: block" border="0">
						<table>
								<tbody>
										<tr>
												<th>十进制</th>
												<th>字符</th>
												<td width="10"> </td>
												<th>十进制</th>
												<th>字符</th>
										</tr>
										<tr>
												<td>32</td>
												<td>space</td>
												<td> </td>
												<td>80</td>
												<td>P</td>
										</tr>
										<tr>
												<td>33</td>
												<td>!</td>
												<td width="10"> </td>
												<td>81</td>
												<td>Q</td>
										</tr>
										<tr>
												<td>34</td>
												<td>"</td>
												<td width="10"> </td>
												<td>82</td>
												<td>R</td>
										</tr>
										<tr>
												<td>35</td>
												<td>#</td>
												<td width="10"> </td>
												<td>83</td>
												<td>S</td>
										</tr>
										<tr>
												<td>36</td>
												<td>$</td>
												<td width="10"> </td>
												<td>84</td>
												<td>T</td>
										</tr>
										<tr>
												<td>37</td>
												<td>%</td>
												<td width="10"> </td>
												<td>85</td>
												<td>U</td>
										</tr>
										<tr>
												<td>38</td>
												<td>&amp;</td>
												<td width="10"> </td>
												<td>86</td>
												<td>V</td>
										</tr>
										<tr>
												<td>39</td>
												<td>'</td>
												<td width="10"> </td>
												<td>87</td>
												<td>w</td>
										</tr>
										<tr>
												<td>40</td>
												<td>(</td>
												<td width="10"> </td>
												<td>88</td>
												<td>X</td>
										</tr>
										<tr>
												<td>41</td>
												<td>)</td>
												<td width="10"> </td>
												<td>89</td>
												<td>Y</td>
										</tr>
										<tr>
												<td>42</td>
												<td>*</td>
												<td width="10"> </td>
												<td>90</td>
												<td>Z</td>
										</tr>
										<tr>
												<td>43</td>
												<td>+</td>
												<td width="10"> </td>
												<td>91</td>
												<td>[</td>
										</tr>
										<tr>
												<td>44</td>
												<td>,</td>
												<td width="10"> </td>
												<td>92</td>
												<td>\</td>
										</tr>
										<tr>
												<td>45</td>
												<td>-</td>
												<td width="10"> </td>
												<td>93</td>
												<td>]</td>
										</tr>
										<tr>
												<td>46</td>
												<td>.</td>
												<td width="10"> </td>
												<td>94</td>
												<td>^</td>
										</tr>
										<tr>
												<td>47</td>
												<td>/</td>
												<td width="10"> </td>
												<td>95</td>
												<td>_</td>
										</tr>
										<tr>
												<td>48</td>
												<td>0</td>
												<td width="10"> </td>
												<td>96</td>
												<td>`</td>
										</tr>
										<tr>
												<td>49</td>
												<td>1</td>
												<td width="10"> </td>
												<td>97</td>
												<td>a</td>
										</tr>
										<tr>
												<td>50</td>
												<td>2</td>
												<td width="10"> </td>
												<td>98</td>
												<td>b</td>
										</tr>
										<tr>
												<td>51</td>
												<td>3</td>
												<td width="10"> </td>
												<td>99</td>
												<td>c</td>
										</tr>
										<tr>
												<td>52</td>
												<td>4</td>
												<td width="10"> </td>
												<td>100</td>
												<td>d</td>
										</tr>
										<tr>
												<td>53</td>
												<td>5</td>
												<td width="10"> </td>
												<td>101</td>
												<td>e</td>
										</tr>
										<tr>
												<td>54</td>
												<td>6</td>
												<td width="10"> </td>
												<td>102</td>
												<td>f</td>
										</tr>
										<tr>
												<td>55</td>
												<td>7</td>
												<td width="10"> </td>
												<td>103</td>
												<td>g </td>
										</tr>
										<tr>
												<td>56</td>
												<td>8</td>
												<td width="10"> </td>
												<td>104</td>
												<td>h</td>
										</tr>
										<tr>
												<td>57</td>
												<td>9</td>
												<td width="10"> </td>
												<td>105</td>
												<td>i</td>
										</tr>
										<tr>
												<td>58</td>
												<td>:</td>
												<td width="10"> </td>
												<td>106</td>
												<td>j</td>
										</tr>
										<tr>
												<td>59</td>
												<td>;</td>
												<td width="10"> </td>
												<td>107</td>
												<td>k</td>
										</tr>
										<tr>
												<td>60</td>
												<td>&lt;</td>
												<td width="10"> </td>
												<td>108</td>
												<td>l</td>
										</tr>
										<tr>
												<td>61</td>
												<td>=</td>
												<td width="10"> </td>
												<td>109</td>
												<td>m</td>
										</tr>
										<tr>
												<td>62</td>
												<td>&gt;</td>
												<td width="10"> </td>
												<td>110</td>
												<td>n</td>
										</tr>
										<tr>
												<td>63</td>
												<td>?</td>
												<td width="10"> </td>
												<td>111</td>
												<td>o</td>
										</tr>
										<tr>
												<td>64</td>
												<td>@</td>
												<td width="10"> </td>
												<td>112</td>
												<td>p</td>
										</tr>
										<tr>
												<td>65</td>
												<td>A</td>
												<td width="10"> </td>
												<td>113</td>
												<td>q</td>
										</tr>
										<tr>
												<td>66</td>
												<td>B</td>
												<td width="10"> </td>
												<td>114</td>
												<td>r</td>
										</tr>
										<tr>
												<td>67</td>
												<td>C</td>
												<td width="10"> </td>
												<td>115</td>
												<td>s</td>
										</tr>
										<tr>
												<td>68</td>
												<td>D</td>
												<td width="10"> </td>
												<td>116</td>
												<td>t</td>
										</tr>
										<tr>
												<td>69</td>
												<td>E</td>
												<td width="10"> </td>
												<td>117</td>
												<td>u</td>
										</tr>
										<tr>
												<td>70</td>
												<td>F</td>
												<td width="10"> </td>
												<td>118</td>
												<td>v</td>
										</tr>
										<tr>
												<td>71</td>
												<td>G</td>
												<td width="10"> </td>
												<td>119</td>
												<td>w</td>
										</tr>
										<tr>
												<td>72</td>
												<td>H</td>
												<td width="10"> </td>
												<td>120</td>
												<td>x</td>
										</tr>
										<tr>
												<td>73</td>
												<td>I</td>
												<td width="10"> </td>
												<td>121</td>
												<td>y</td>
										</tr>
										<tr>
												<td>74</td>
												<td>J</td>
												<td width="10"> </td>
												<td>122</td>
												<td>z</td>
										</tr>
										<tr>
												<td>75</td>
												<td>K</td>
												<td width="10"> </td>
												<td>123</td>
												<td>{</td>
										</tr>
										<tr>
												<td>76</td>
												<td>L</td>
												<td width="10"> </td>
												<td>124</td>
												<td>|</td>
										</tr>
										<tr>
												<td>77</td>
												<td>M</td>
												<td width="10"> </td>
												<td>125</td>
												<td>}</td>
										</tr>
										<tr>
												<td>78</td>
												<td>N</td>
												<td width="10"> </td>
												<td>126</td>
												<td>~</td>
										</tr>
										<tr>
												<td>79</td>
												<td>O</td>
												<td width="10"> </td>
												<td>127</td>
												<td>DEL</td>
										</tr>
								</tbody>
						</table>
				</span>
				<h2>扩展 ASCII 打印字符</h2>
				<p>扩展的 ASCII 字符满足了对更多字符的需求。扩展的 ASCII 包含 ASCII 中已有的 128 个字符（数字 0–32 显示在下图中），又增加了 128 个字符，总共是 256 个。即使有了这些更多的字符，许多语言还是包含无法压缩到 256 个字符中的符号。因此，出现了一些 ASCII 的变体来囊括地区性字符和符号。</p>
				<p>例如，许多软件程序把 ASCII 表（又称作 ISO 8859-1）用于北美、西欧、澳大利亚和非洲的语言。</p>
				<p>
						<a class="DropDown" href="javascript:ToggleDiv('divExpCollAsst_2')">
								<img id="divExpCollAsst_2_img" alt="隐藏" src="http://office.microsoft.com/global/images/bluedrop.gif" border="0" />
								<font color="#000080">扩展的 ASCII 打印字符表</font>
						</a>
				</p>
				<span class="ACECollapsed" id="divExpCollAsst_2" style="DISPLAY: block" border="0">
						<table>
								<tbody>
										<tr>
												<th>十进制</th>
												<th>字符</th>
												<td width="10"> </td>
												<th>十进制</th>
												<th>字符</th>
										</tr>
										<tr>
												<td>128</td>
												<td>Ç</td>
												<td> </td>
												<td>192</td>
												<td>└</td>
										</tr>
										<tr>
												<td>129</td>
												<td>ü</td>
												<td width="10"> </td>
												<td>193</td>
												<td>┴</td>
										</tr>
										<tr>
												<td>130</td>
												<td>é</td>
												<td width="10"> </td>
												<td>194</td>
												<td>┬</td>
										</tr>
										<tr>
												<td>131</td>
												<td>â</td>
												<td width="10"> </td>
												<td>195</td>
												<td>├</td>
										</tr>
										<tr>
												<td>132</td>
												<td>ä</td>
												<td width="10"> </td>
												<td>196</td>
												<td>─</td>
										</tr>
										<tr>
												<td>133</td>
												<td>à</td>
												<td width="10"> </td>
												<td>197</td>
												<td>┼</td>
										</tr>
										<tr>
												<td>134</td>
												<td>å</td>
												<td width="10"> </td>
												<td>198</td>
												<td>╞</td>
										</tr>
										<tr>
												<td>135</td>
												<td>ç</td>
												<td width="10"> </td>
												<td>199</td>
												<td>╟</td>
										</tr>
										<tr>
												<td>136</td>
												<td>ê</td>
												<td width="10"> </td>
												<td>200</td>
												<td>╚</td>
										</tr>
										<tr>
												<td>137</td>
												<td>ë</td>
												<td width="10"> </td>
												<td>201</td>
												<td>╔</td>
										</tr>
										<tr>
												<td>138</td>
												<td>è</td>
												<td width="10"> </td>
												<td>202</td>
												<td>╩</td>
										</tr>
										<tr>
												<td>139</td>
												<td>ï</td>
												<td width="10"> </td>
												<td>203</td>
												<td>╦</td>
										</tr>
										<tr>
												<td>140</td>
												<td>î</td>
												<td width="10"> </td>
												<td>204</td>
												<td>╠</td>
										</tr>
										<tr>
												<td>141</td>
												<td>ì</td>
												<td width="10"> </td>
												<td>205</td>
												<td>═</td>
										</tr>
										<tr>
												<td>142</td>
												<td>Ä</td>
												<td width="10"> </td>
												<td>206</td>
												<td>╬</td>
										</tr>
										<tr>
												<td>143</td>
												<td>Å</td>
												<td width="10"> </td>
												<td>207</td>
												<td>╧</td>
										</tr>
										<tr>
												<td>144</td>
												<td>É</td>
												<td width="10"> </td>
												<td>208</td>
												<td>╨</td>
										</tr>
										<tr>
												<td>145</td>
												<td>æ</td>
												<td width="10"> </td>
												<td>209</td>
												<td>╤</td>
										</tr>
										<tr>
												<td>146</td>
												<td>Æ</td>
												<td width="10"> </td>
												<td>210</td>
												<td>╥</td>
										</tr>
										<tr>
												<td>147</td>
												<td>ô</td>
												<td width="10"> </td>
												<td>211</td>
												<td>╙</td>
										</tr>
										<tr>
												<td>148</td>
												<td>ö</td>
												<td width="10"> </td>
												<td>212</td>
												<td>Ô</td>
										</tr>
										<tr>
												<td>149</td>
												<td>ò</td>
												<td width="10"> </td>
												<td>213</td>
												<td>╒</td>
										</tr>
										<tr>
												<td>150</td>
												<td>û</td>
												<td width="10"> </td>
												<td>214</td>
												<td>╓</td>
										</tr>
										<tr>
												<td>151</td>
												<td>ù</td>
												<td width="10"> </td>
												<td>215</td>
												<td>╫</td>
										</tr>
										<tr>
												<td>152</td>
												<td>ÿ</td>
												<td width="10"> </td>
												<td>216</td>
												<td>╪</td>
										</tr>
										<tr>
												<td>153</td>
												<td>Ö</td>
												<td width="10"> </td>
												<td>217</td>
												<td>┘</td>
										</tr>
										<tr>
												<td>154</td>
												<td>Ü</td>
												<td width="10"> </td>
												<td>218</td>
												<td>┌</td>
										</tr>
										<tr>
												<td>155</td>
												<td>¢</td>
												<td width="10"> </td>
												<td>219</td>
												<td>█</td>
										</tr>
										<tr>
												<td>156</td>
												<td>£</td>
												<td width="10"> </td>
												<td>220</td>
												<td>▄</td>
										</tr>
										<tr>
												<td>157</td>
												<td>¥</td>
												<td width="10"> </td>
												<td>221</td>
												<td>▌</td>
										</tr>
										<tr>
												<td>158</td>
												<td>₧</td>
												<td width="10"> </td>
												<td>222</td>
												<td>▐</td>
										</tr>
										<tr>
												<td>159</td>
												<td>ƒ</td>
												<td width="10"> </td>
												<td>223</td>
												<td>▀</td>
										</tr>
										<tr>
												<td>160</td>
												<td>á</td>
												<td width="10"> </td>
												<td>224</td>
												<td>α</td>
										</tr>
										<tr>
												<td>161</td>
												<td>í</td>
												<td width="10"> </td>
												<td>225</td>
												<td>ß</td>
										</tr>
										<tr>
												<td>162</td>
												<td>ó</td>
												<td width="10"> </td>
												<td>226</td>
												<td>Γ</td>
										</tr>
										<tr>
												<td>163</td>
												<td>ú</td>
												<td width="10"> </td>
												<td>227</td>
												<td>π</td>
										</tr>
										<tr>
												<td>164</td>
												<td>ñ</td>
												<td width="10"> </td>
												<td>228</td>
												<td>Σ</td>
										</tr>
										<tr>
												<td>165</td>
												<td>Ñ</td>
												<td width="10"> </td>
												<td>229</td>
												<td>σ</td>
										</tr>
										<tr>
												<td>166</td>
												<td>ª</td>
												<td width="10"> </td>
												<td>230</td>
												<td>µ</td>
										</tr>
										<tr>
												<td>167</td>
												<td>º</td>
												<td width="10"> </td>
												<td>231</td>
												<td>τ</td>
										</tr>
										<tr>
												<td>168</td>
												<td>¿</td>
												<td width="10"> </td>
												<td>232</td>
												<td>Φ</td>
										</tr>
										<tr>
												<td>169</td>
												<td>⌐</td>
												<td width="10"> </td>
												<td>233</td>
												<td>Θ</td>
										</tr>
										<tr>
												<td>170</td>
												<td>¬</td>
												<td width="10"> </td>
												<td>234</td>
												<td>Ω</td>
										</tr>
										<tr>
												<td>171</td>
												<td>½</td>
												<td width="10"> </td>
												<td>235</td>
												<td>δ</td>
										</tr>
										<tr>
												<td>172</td>
												<td>¼</td>
												<td width="10"> </td>
												<td>236</td>
												<td>∞</td>
										</tr>
										<tr>
												<td>173</td>
												<td>¡</td>
												<td width="10"> </td>
												<td>237</td>
												<td>φ</td>
										</tr>
										<tr>
												<td>174</td>
												<td>«</td>
												<td width="10"> </td>
												<td>238</td>
												<td>ε</td>
										</tr>
										<tr>
												<td>175</td>
												<td>»</td>
												<td width="10"> </td>
												<td>239</td>
												<td>∩</td>
										</tr>
										<tr>
												<td>176</td>
												<td>░</td>
												<td width="10"> </td>
												<td>240</td>
												<td>≡</td>
										</tr>
										<tr>
												<td>177</td>
												<td>▒</td>
												<td width="10"> </td>
												<td>241</td>
												<td>±</td>
										</tr>
										<tr>
												<td>178</td>
												<td>▓</td>
												<td width="10"> </td>
												<td>242</td>
												<td>≥</td>
										</tr>
										<tr>
												<td>179</td>
												<td>│</td>
												<td width="10"> </td>
												<td>243</td>
												<td>≤</td>
										</tr>
										<tr>
												<td>180</td>
												<td>┤</td>
												<td width="10"> </td>
												<td>244</td>
												<td>⌠</td>
										</tr>
										<tr>
												<td>181</td>
												<td>╡</td>
												<td width="10"> </td>
												<td>245</td>
												<td>⌡</td>
										</tr>
										<tr>
												<td>182</td>
												<td>╢</td>
												<td width="10"> </td>
												<td>246</td>
												<td>÷</td>
										</tr>
										<tr>
												<td>183</td>
												<td>╖</td>
												<td width="10"> </td>
												<td>247</td>
												<td>≈</td>
										</tr>
										<tr>
												<td>184</td>
												<td>╕</td>
												<td width="10"> </td>
												<td>248</td>
												<td>≈</td>
										</tr>
										<tr>
												<td>185</td>
												<td>╣</td>
												<td width="10"> </td>
												<td>249</td>
												<td>∙</td>
										</tr>
										<tr>
												<td>186</td>
												<td>║</td>
												<td width="10"> </td>
												<td>250</td>
												<td>·</td>
										</tr>
										<tr>
												<td>187</td>
												<td>╗</td>
												<td width="10"> </td>
												<td>251</td>
												<td>√</td>
										</tr>
										<tr>
												<td>188</td>
												<td>╝</td>
												<td width="10"> </td>
												<td>252</td>
												<td>ⁿ</td>
										</tr>
										<tr>
												<td>189</td>
												<td>╜</td>
												<td width="10"> </td>
												<td>253</td>
												<td>²</td>
										</tr>
										<tr>
												<td>190</td>
												<td>╛</td>
												<td width="10"> </td>
												<td>254</td>
												<td>■</td>
										</tr>
										<tr>
												<td>191</td>
												<td>┐</td>
												<td width="10"> </td>
												<td>255</td>
												<td> </td>
										</tr>
								</tbody>
						</table>
				</span>
				<h2>ASCII 非打印控制字符</h2>
				<p>ASCII 表上的数字 0–31 分配给了控制字符，用于控制像打印机等一些外围设备。例如，12 代表换页/新页功能。此命令指示打印机跳到下一页的开头。</p>
				<p>
						<a class="DropDown" href="javascript:ToggleDiv('divExpCollAsst_3')">
								<img id="divExpCollAsst_3_img" alt="隐藏" src="http://office.microsoft.com/global/images/bluedrop.gif" border="0" />
								<font color="#000080">ASCII 非打印控制字符表</font>
						</a>
				</p>
				<span class="ACECollapsed" id="divExpCollAsst_3" style="DISPLAY: block" border="0">
						<table>
								<tbody>
										<tr>
												<th>十进制</th>
												<th>字符</th>
												<td width="10"> </td>
												<th>十进制</th>
												<th>字符</th>
										</tr>
										<tr>
												<td>0</td>
												<td>空</td>
												<td> </td>
												<td>16</td>
												<td>数据链路转意</td>
										</tr>
										<tr>
												<td>1</td>
												<td>头标开始</td>
												<td width="10"> </td>
												<td>17</td>
												<td>设备控制 1</td>
										</tr>
										<tr>
												<td>2</td>
												<td>正文开始</td>
												<td width="10"> </td>
												<td>18</td>
												<td>设备控制 2</td>
										</tr>
										<tr>
												<td>3</td>
												<td>正文结束</td>
												<td width="10"> </td>
												<td>19</td>
												<td>设备控制 3</td>
										</tr>
										<tr>
												<td>4</td>
												<td>传输结束</td>
												<td width="10"> </td>
												<td>20</td>
												<td>设备控制 4</td>
										</tr>
										<tr>
												<td>5</td>
												<td>查询</td>
												<td width="10"> </td>
												<td>21</td>
												<td>反确认</td>
										</tr>
										<tr>
												<td>6</td>
												<td>确认</td>
												<td width="10"> </td>
												<td>22</td>
												<td>同步空闲</td>
										</tr>
										<tr>
												<td>7</td>
												<td>震铃</td>
												<td width="10"> </td>
												<td>23</td>
												<td>传输块结束</td>
										</tr>
										<tr>
												<td>8</td>
												<td>backspace</td>
												<td width="10"> </td>
												<td>24</td>
												<td>取消</td>
										</tr>
										<tr>
												<td>9</td>
												<td>水平制表符</td>
												<td width="10"> </td>
												<td>25</td>
												<td>媒体结束</td>
										</tr>
										<tr>
												<td>10</td>
												<td>换行/新行</td>
												<td width="10"> </td>
												<td>26</td>
												<td>替换</td>
										</tr>
										<tr>
												<td>11</td>
												<td>竖直制表符</td>
												<td width="10"> </td>
												<td>27</td>
												<td>转意</td>
										</tr>
										<tr>
												<td>12</td>
												<td>换页/新页</td>
												<td width="10"> </td>
												<td>28</td>
												<td>文件分隔符</td>
										</tr>
										<tr>
												<td>13</td>
												<td>回车</td>
												<td width="10"> </td>
												<td>29</td>
												<td>组分隔符</td>
										</tr>
										<tr>
												<td>14</td>
												<td>移出</td>
												<td width="10"> </td>
												<td>30</td>
												<td>记录分隔符</td>
										</tr>
										<tr>
												<td>15</td>
												<td>移入</td>
												<td width="10"> </td>
												<td>31</td>
												<td>单元分隔符</td>
										</tr>
								</tbody>
						</table>
				</span>
				<h2>ASCII 之外</h2>
				<p>另一个更新的字符表称为 <a class="glossary" href="javascript:AppendPopup(this,'ofUnicode_5')"><font color="#000080">Unicode<span class="AsstInlineDefText"><span class="ACICollapsed" id="divInlineDef_ofUnicode_5"> （Unicode：Unicode Consortium 开发的一种字符编码标准。该标准采用多（于一）个字节代表每一字符，实现了使用单个字符集代表世界上几乎所有书面语言。）</span></span></font></a>。 因为 Unicode 表大得多，它可以表示 65,536 个字符，而 ASCII 表只能表示 128 个字符，扩展的 ASCII 表也只能表示 256 个字符。这一更大的容量使不同语言的大多数字符都能包含在同一个字符集中。</p>
		</div>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/21741.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-01-10 22:31 <a href="http://www.cnitblog.com/zouzheng/articles/21741.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>TCP/IP基础及详解 </title><link>http://www.cnitblog.com/zouzheng/articles/21739.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Wed, 10 Jan 2007 14:26:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/21739.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/21739.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/21739.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/21739.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/21739.html</trackback:ping><description><![CDATA[
		<div class="posttext">很多不同的厂家生产各种型号的计算机，它们运行完全不同的操作系统，但TCP/IP协议组件允许它们互相进行通信。这一点很让人感到吃惊，因为它的作用已远远超出了起初的设想。TCP/IP起源于60年代末美国政府资助的一个分组交换网络研究项目，到现在90年代已发展成为计算机之间最常应用的组网形式。它是一个真正的开放系统，因为协议组件的定义及其多种实现可以不用花钱或花很少的钱就可以公开地得到。它成为被称作"全球互联网"或"因特网"(Internet)的基础，该广域网（WAN）已包含超过100万台遍布世界各地的计算机。 <br />一、.分层 <br />网络协议通常分不同层次进行开发，每一层分别负责不同的通信功能。一个协议组件，比如TCP/IP，是一组不同层次上的多个协议的组合。TCP/IP通常被认为是一个四层协议系统。 <br />TCP/IP协议组件的四个层次 <br />1. 链路层，有时也称作数据链路层或网络接口层，通常包括操作系统中的设备驱动程序和计算机中对应的网络接口卡。它们一起处理与电缆（或其他任何传输媒介）的物理接口细节。 <br />2. 网络层，有时也称作互连网层，处理分组在网络中的活动，例如分组的路由选择。在TCP/IP协议组件中，网络层协议包括IP协议（网际协议），ICMP协议（Internet互连网控制报文协议），以及IGMP协议（Internet组管理协议）。 <br />3. 运输层,主要为两台主机上的应用程序提供端到端的通信。在TCP/IP协议组件中，有两个互不相同的传输协议：TCP（传输控制协议）和UDP（用户数据报协议）。 <br />TCP为两台主机提供高可靠性的数据通信。它所做的工作包括把应用程序交给它的数据分成合适的小块交给下面的网络层，确认接收到的分组，设置发送最后确认分组的超时时钟等。由于运输层提供了高可靠性的端到端的通信，因此应用层可以忽略所有这些细节。 <br />而另一方面，UDP则为应用层提供一种非常简单的服务。它只是把称作数据报的分组从一台主机发送到另一台主机，但并不保证该数据报能到达另一端。任何必需的可靠性必须由应用层来提供。 <br />这两种运输层协议分别在不同的应用程序中有不同的用途。 <br />4. 应用层,负责处理特定的应用程序细节。几乎各种不同的TCP/IP实现都会提供下面这些通用的应用程序： <br />•Telnet 远程登录 <br />•FTP 文件传输协议 <br />•SMTP 用于电子邮件的简单邮件传输协议 <br />•SNMP 简单网络管理协议</div>
		<div class="posttext">
				<div class="posttext">二、通过路由器连接的两个网络 <br />在TCP/IP协议组件中，网络层IP提供的是一种不可靠的服务。也就是说，它只是尽可能快地把分组从源结点送到目的结点，但是并不提供任何可靠性保证。而另一方面，TCP在不可靠的IP层上提供了一个可靠的运输层。为了提供这种可靠的服务，TCP采用了超时重传，发送和接收端到端的确认分组等机制。由此可见，运输层和网络层分别负责不同的功能。 <br />从定义上看，一个路由器具有两个或多个网络接口层（因为它连接了两个或多个网络）。任何具有多个接口的系统英文都称作是多接口的multihomed。一个主机也可以有多个接口，但一般不称作路由器, 除非它的功能只是单纯地把分组从一个 接口传送到另一个接口。同样，路由器并不一定指那种在互连网中用来转发分组的特殊硬件盒。大多数的TCP/IP实现也允许一个多接口主机来担当路由器的功能，但是主机为此必须进行特殊的配置。在这种情况下，我们既可以称该系统为主机（当它运行某一应用程序时，如FTP或Telnet），也可以称之为路由器（当它把分组从一个网络转发到另一个网络时）。我们在不同的场合下使用不同的术语。 <br />互连网的目标之一是在应用程序中隐藏所有的物理细节。虽然这一点在由两个网络组成的互连网中并不很明显，但是应用层不能关心（也不关心）一台主机是在以太网上，而另一台主机是在令牌环网上，它们通过路由器进行互连。随着增加不同类型的物理网络，可能会有20个路由器，但应用层仍然是一样的。物理细节的隐藏使得互连网功能非常强大，也非常有用。 <br />连接网络的另一个途径是使用网桥。网桥是在链路层上对网络进行互连，而路由器则是在网络层上对网络进行互连。网桥使得多个局域网（LAN）组合在一起，这样对上层来说就好像是一个局域网。 <br />TCP /IP倾向于使用路由器而不是网桥来连接网络</div>
				<div class="posttext">
						<div class="posttext">三、TCP/IP协议组件 <br />TCP和UDP是两种最为著名的运输层协议，二者都使用IP作为网络层协议。 <br />虽然TCP使用不可靠的IP服务，但它却提供一种可靠的运输层服务。 <br />UDP为应用程序发送和接收数据报。一个数据报是指从发送方传输到接收方的一个信息单元（例如，发送方指定的一定字节数的信息）。但是与TCP不同的是，UDP是不可靠的，它不能保证数据报能安全无误地到达最终目的。SNMP（简单网络管理协议）也使用了UDP协议，但是由于它还要处理许多其他的协议，因此留到后面再进行讨论。 <br />IP是网络层上的主要协议，同时被TCP和UDP使用。TCP和UDP的每组数据都通过端系统和每个中间路由器中的IP层在互连网中进行传输。 <br />ICMP是IP协议的附属协议。IP层用它来与其他主机或路由器交换错误报文和其他重要信息。尽管ICMP主要被IP使用，但应用程序也有可能访问它。两个流行的诊断工具，Ping和Traceroute，它们都使用了ICMP。 <br />IGMP是Internet组管理协议。它用来把一个UDP数据报广播（把一个UDP数据报发送到某个指定网络上的所有主机）到多个主机。 <br />ARP（地址解析协议）和RARP（逆地址解析协议）是某些网络接口（如以太网和令牌环网）使用的特殊协议，用来转换IP层和网络接口层使用的地址。</div>
						<div class="posttext">
								<div class="posttext">四、互连网的地址 <br />互连网上的每个接口必须有一个唯一的Internet地址（也称作IP地址）。IP地址长32 bit。Internet地址并不采用平面形式的地址空间，如1，2，3等。IP地址具有一定的结构. 这些32位的地址通常写成四个十进制的数，其中每个整数对应一个字节。这种表示方法称作“点分十进制表示法”（dotted decimal notation）。 <br />需要再次指出的是，多接口主机具有多个IP地址，其中每个接口都对应一个IP地址。 <br />由于互连网上的每个接口必须有一个唯一的IP地址，因此必须要有一个管理机构为接入互连网的网络分配IP地址。这个管理机构就是互连网络信息中心（Internet Network Information Centre）称作InterNIC。InterNIC只分配网络号。主机号的分配由系统管理员来负责。 <br />Internet注册服务(IP地址和DNS域名)过去由NIC来负责，其网络地址是nic.ddn.mil。1993年4月1日，InterNIC成立。现在，NIC只负责处理国防数据网的注册请求，所有其他的Internet用户注册请求均由InterNIC负责处理，其网址是：rs.internic.net。 <br />事实上InterNIC有三部分组成：注册服务（rs.internic.net），目录和数据库服务（ds.internic.net），以及信息服务（is.internic.net）。 <br />IP地址有三类：单目传送地址（目标为单个主机），广播传送地址（目的端为给定网络上的所有主机），以及多目传送地址（目的端为同一组内的所有主机）。</div>
								<div class="posttext">五、域名系统 <br />尽管通过IP地址可以识别主机上的网络接口，进而访问主机，但是人们最喜欢使用的还是主机名。在TCP/IP领域中，域名系统（DNS）是一个分布的数据库，由它来提供IP地址和主机名之间的映射信息。 <br />现在，我们必须理解，任何应用程序都可以调用一个标准的库函数来查看给定名字的主机的IP地址。类似地，系统还提供一个逆函数――给定主机的IP地址，查看它所对应的主机名。 <br />大多数使用主机名作为参数的应用程序也可以把IP地址作为参数。如，当我们用Telnet进行远程登录时，我们既可以指定一个主机名，也可以指定一个IP地址。</div>
								<div class="posttext">
										<div class="posttext">六、封装 <br />当应用程序用TCP传送数据时，数据被送入协议栈中，然后逐个通过每一层直到被当作一串比特流送入网络。其中每一层对收到的数据都要增加一些首部信息（有时还要增加尾部信息）。TCP传给IP的数据单元称作TCP报文段或简称为TCP段（TCP segment）。IP传给网络接口层的数据单元称作IP数据报(IP datagram)。通过以太网传输的比特流称作帧(frame)。以太网数据帧的物理特性是其长度必须在46－1500字节之间。 <br />所有的Internet标准和大多数有关TCP/IP的书都使用octet这个术语来表示字节。使用这个过分雕琢的术语是有历史原因的，因为TCP/IP的很多工作都是在DEC-10系统上进行的，但是它并不使用8 bit的字节。由于现在几乎所有的计算机系统都采用8 bit的字节，因此我们在此使用字节（byte）这个术语。 <br />更准确地说，IP和网络接口层之间传送的数据单元应该是分组（packet）。分组既可以是一个IP数据报，也可以是IP数据报的一个片（fragment）。 <br />UDP数据与TCP数据基本一致。唯一的不同是UDP传给IP的信息单元称作UDP数据报（UDP datagram），而且UDP的首部长为8字节。 <br />由于TCP，UDP，ICMP和IGMP都要向IP传送数据，因此IP必须在生成的IP首部中加入某种标识，以表明数据属于哪一层。为此，IP在首部中存入一个长度为8比特的数值，称作协议域。1表示为ICMP协议，2表示为IGMP协议，6表示为TCP协议，17表示为UDP协议。 <br />类似地，许多应用程序都可以使用TCP或UDP来传送数据。运输层协议在生成报文首部时要存入一个应用程序的标识符。TCP和UDP都用一个16 bit的端口号来表示不同的应用程序。TCP和UDP把源端口号和目的端口号分别存入报文首部中。 <br />网络接口分别要发送和接收IP，ARP和RARP数据，因此也必须在以太网的帧首部中加入某种形式的标识，以指明生成数据的网络层协议。为此，以太网的帧首部也有一个16 bit的帧类型域。</div>
										<div class="posttext">
												<div class="posttext">七、分用（Demultiplexing） <br />当目的主机收到一个以太网数据帧时，数据就开始从协议栈中由底向上升，同时去掉各层协议加上的报文首部。每层协议盒都要去检查报文首部中的协议标识，以确定接收数据的上层协议。这个过程称作分用。 <br />为协议ICMP和IGMP定位一直是一件很棘手的事情。我们把协议ICMP和IGMP与IP放在同一层上，那是因为事实上它们是IP的附属协议。但是我们又把它们放在IP层的上面，这是因为ICMP和IGMP报文都被封装在IP数据报中。 <br />对于ARP和RARP我们也遇到类似的难题。在这里我们把它们放在以太网设备驱动程序的上方，这是因为它们和IP数据报一样，都有各自的以太网数据帧类型。但，我们又把ARP作为以太网设备驱动程序的一部分，放在IP层的下面，其原因在逻辑上是合理的。 <br />当进一步描述TCP的细节时，我们将看到协议确实是通过目的端口号，源IP地址和源端口号进行解包的。</div>
												<div class="posttext">
														<div class="posttext">八、客户服务器模型 <br />大部分网络应用程序在编写时都假设一端是客户，另一端是服务器，其目的是为了让服务器为客户提供一些特定的服务。 <br />我们可以将这种服务分为两种类型：重复型或并发型。 <br />重复型服务器通过以下步骤进行交互： <br />1. 等待一个客户请求的到来。 <br />2. 处理客户请求。 <br />3. 发送响应给发送请求的客户。 <br />4. 返回步骤1。 <br />重复型服务器主要的问题发生在2状态。在这个时候，它不能为其他客户机提供服务。 <br />并发型服务器采用以下步骤： <br />1. 等待一个客户请求的到来 <br />2. 启动一个新的服务器来处理这个客户的请求。在这期间可能生成一个新的进程、任务或线程，并依赖底层操作系统的支持。这个步骤如何进行取决于操作系统。生成的新服务器对客户的全部请求进行处理。处理结束后，终止这个新服务器。 <br />3.返回步骤1。 <br />并发服务器的优点在于它是利用生成其他服务器的方法来处理客户的请求。也就是说，每个客户都有它自己对应的服务器。如果操作系统允许多任务，那么就可以同时为多个客户同时服务。 <br />我们对服务器，而不是对客户进行分类的原因是因为对于一个客户来说，它通常并不能够辨别自己是与一个重复型服务器或并发型服务器进行对话。 <br />一般来说，TCP服务器是并发的，而UDP服务器是重复的，但也存在一些例外。</div>
														<div class="posttext">
																<div class="posttext">九、端口号 <br />我们前面已经指出过，TCP和UDP采用16比特的端口号来识别应用程序。那么这些端口号是如何选择的呢？ <br />服务器一般都是通过人们所熟知的端口号来识别的。例如，对于每个TCP/IP实现来说，FTP服务器的TCP端口号都是21，每个Telnet服务器的TCP端口号都是23，每个TFTP(简单文件传输协议)服务器的UDP端口号都是69。任何TCP/IP实现所提供的服务都用众所周知的1－1023之间的端口号。这些人们所熟知的端口号由Internet端口号分配机构（Internet Assigned Numbers Authority, IANA）来管理。 <br />到1992年为止，人们所熟知的端口号介于1－255之间。256－1023之间的端口号通常都是由Unix系统占用，以提供一些特定的Unix服务――也就是说，提供一些只有Unix系统才有的，而其他操作系统可能不提供的服务。现在IANA管理1－1023之间所有的端口号。 <br />Internet扩展服务与Unix特定服务之间的一个差别就是Telnet和Rlogin。它们二者都允许我们通过计算机网络登录到其他主机上。Telnet是采用端口号为23的TCP/IP标准且几乎可以在所有操作系统上进行实现。相反，Rlogin最开始时只是为Unix系统设计的（尽管许多非Unix系统现在也提供该服务），因此在80年代初，它的有名端口号为513。 <br />客户端通常对它所使用的端口号并不关心，只需保证该端口号在本机上是唯一的就可以了。客户端口号又称作临时端口号（即存在时间很短暂）。这是因为它通常只是在用户运行该客户程序时才存在，而服务器则只要主机开着的，其服务就运行。 <br />大多数TCP/IP实现给临时端口分配1024－5000之间的端口号。大于5000的端口号是为其他服务器预留的（Internet上并不常用的服务)。 <br />保留端口号 <br />Unix系统有保留端口号的概念。只有具有超级用户特权的进程才允许给它自己分配一个保留端口号。 <br />这些端口号介于1到1023之间，一些应用程序（如有名的Rlogin）将它作为客户与服务器之间身份认证的一部分。</div>
																<div class="posttext">
																		<div class="posttext">十、标准化过程 <br />究竟是谁控制着TCP/IP协议组件，又是谁在定义新的标准以及其他类似的事情？事实上，有四个小组在负责Internet技术。 <br />1. Internet协会（ISOC: Internet Society）是一个推动、支持和促进Internet不断增长和发展的专业组织，它把Internet作为全球研究通信的基础设施。 <br />2. Internet体系结构委员会（IAB：Internet Architecture Board）是一个技术监督和协调的机构。它由国际上来自不同专业的15个志愿者组成，其职能是负责Internet标准的最后编辑和技术审核。IAB隶属于ISOC。 <br />3. Internet工程专门小组（IETF：Internet Engineering Task Force）是一个面向近期标准的组织，它分为9个领域（应用，寻径和寻址，安全等等）。IETF开发成为Internet标准的规约。为帮助IETF主席，又成立了Internet工程指导小组（IESG：Internet Engineering Steering Group）。 <br />4. Internet研究专门小组主要对长远的项目进行研究。 <br />IRTF和IETF都隶属于IAB。 <br />　 <br />RFC <br />所有关于Internet的正式标准都以RFC（Request for Comment）文档出版。另外，大量的RFC并不是正式的标准，出版的目的只是为了提供信息。RFC的篇幅从1页到200页不等。每一项都用一个数字来标识，如RFC 1122，数字越大说是RFC的内容越新。 <br />所有的RFC都可以通过电子邮件或用FTP从Internet上免费获取。如果发送下面这份电子邮件，你就会收到一份获取RFC的方法清单： <br />To: rfc-info@ISI.EDU <br />Subject: getting rfcs <br />help: ways_to_get_rfcs <br />最新的RFC索引总是搜索信息的起点。这个索引列出了RFC被替换或局部更新的时间。 <br />下面是一些重要的RFC文档： <br />1. 赋值RFC（Assigned Numbers RFC）列出了所有Internet协议中使用的数字和常数。至本书出版时为止，最新RFC的编号是1340 [Reynolds and Postel 1992]。所有著名的Internet端口号都列在这里。 <br />当这个RFC被更新时（通常每年至少更新一次），索引清单会列出RFC 1340被替换的时间。 <br />2. Internet正式协议标准，目前是RFC 1600[Postel 1994]。这个RFC描述了各种Internet协议的标准化现状。每种协议都处于下面几种标准化状态之一：标准，草案标准，提议标准，实验标准，信息标准，和历史标准。另外，对每种协议都有一个要求的层次：必需的，建议的，可选择的，限制使用的，或者不推荐的。 <br />与赋值RFC一样，这个RFC也定期更新。请一定随时查看最新版本。 <br />3. 主机需求RFC，1122和1123[Braden 1989a, 1989b]。RFC 1122针对链路层，网络层和运输层，RFC 1123针对应用层。这两个RFC对早期重要的RFC文档作了大量的纠正和解释。如果要查看有关协议更详细的细节内容，它们通常是一个入口点。它们列出了协议中关于“必须”，“应该”，“可以”，“不应该”或者“不能”等特性及其实现细节。 <br />RFC 1127[Braden 1989c]对工作小组开发主机需求RFC过程中的讨论内容和结论进行了非正式的总结。 <br />4．路由器需求RFC，目前正式版是RFC 1009[Braden and Postel 1987]，但一个新版已接近完成[Aknqyust 1993]。它与主机需求RFC类似，但是只单独描述了路由器的需求。 <br />标准的简单服务 <br />有一些标准的简单服务几乎每种实现都要提供。而客户程序通常选择Telnet。当使用TCP和UDP提供相同的服务时，一般选择相同的端口号. <br />如果仔细检查这些标准的简单服务以及其他标准的TCP/IP服务（如Telnet, FTP, SMTP等）的端口号时，我们发现它们都是奇数。这是有历史原因的，因为这些端口号都是从NCP端口号派生出来的。（NCP，即网络控制协议，是ARPANET的运输层协议，是TCP的前身。NCP是单工的，不是全双工的，因此每个应用程序需要两个连接，需预留一对奇数和偶数端口号。当TCP和UDP成为标准的运输层协议时，每个应用程序只需要一个端口号，因此就使用了NCP中的奇数。</div>
																		<div class="posttext">
																				<div class="posttext">十一、互连网 <br />世界范围内的互连网－Internet , internet这个词第一个字母是否大写决定了它具有不同的含义。 <br />internet意思是用一个共同的协议族把多个网络连接在一起。而Internet指的是世界范围内通过TCP/IP互相通信的所有主机集合（超过100万台）。Internet是一个internet（互连网），但internet不等于Internet。 <br /><br />十二、实现 <br />既成事实标准的TCP/IP软件实现来自于位于伯克利的加利福尼亚大学的计算机系统研究小组。从历史上看，软件是随同4.x BSD系统（Berkeley Software Distribution）的网络版一起发布的。它的源代码是许多其他实现的基础。 <br />下面列举了各种BSD版本发布的时间，并标注了重要的TCP/IP特性。列在左边的BSD网络版，其所有的网络源代码可以公开得到：包括协议本身以及许多应用程序和工具（如Telnet和FTP）。 <br /><br />4.2BSD (1983) 第一个广泛可用的TCP/IP发布 <br />4.3BSD (1986) TCP性能得到改善 <br />4.3BSD Tahoe (1988) 启动慢，拥塞避免措施 <br />BSD网络软件1.0版(1989)：Net/1 <br />4.3BSD Reno(1990) TCP首部预测，SLIP首部压缩 路由表修改 <br />BSD网络软件2.0版(1991)：Net/2 <br />4.4BSD(1993) 多播，长胖管道修改 <br />4.4BSD-Lite (1994) 又称为Net/3 <br /><br />十三、应用编程接口 <br />使用TCP/IP协议的应用程序通常采用两种应用编程接口（API）：socket和TLI（运输层接口：Transport Layer Interface）。前者有时称作“Berkeley socket”，表明它是从伯克利版发展而来的。后者起初是由AT&amp;T开发的，有时称作XTI（X/Open传输接口），以承认X/Open这个自己定义标准的国际计算机生产产商所做的工作。XTI实际上是TLI的一个超集。</div>
																				<div class="posttext">
																						<div class="posttext">（一）<br />TCP/IP应该是个协议集，根据OS的七层理论，TCP/IP分为四层．分别是应用，传输，Interne和网络界面．<br /><br />我们一般说TCP在传输层，而IP在Internet层．<br /><br />TCP/IP的应用程序包括我们平时经常用到的Ping,Telnet,Ftp,Finger等等<br /><br />配置TCP/IP包括IP地址，子网掩码和缺省网关<br /><br />正确检测TCP/IP的四个步骤：PIng 127.0.0.1（回环地址）如果通表示TCP/IP已经装入，Ping自己表明客户机正常（主要是网卡），Ping网关表示局域网正常，Ping路由外地址表示完全正常，当然你也可以直接进行第四步，一般来说没这么麻烦的，但理论是基础:-)<br /><br />IP地址是四段八位的二进制数组成的，IP分为A,B,C,D,E五类地址<br /><br />A类高端为0,从1.x.y.z~126.x.y.z　.B类高端为10,从128.x.y.z~191.x.y.z　C类高端为110，从192.x.y.z~223.x.y.z　D类高端为1110是保留的IP地址　E类高端为1111，是科研用的IP地址<br /><br />其中255是广播地址，127是内部回送地址</div>
																						<div class="posttext">
																								<div class="posttext">（二）<br />以下内容是子网的设定<br />若公司不上Internet,那一定不会烦恼IPAddress的问题,因 为可以任意使用所有的IPAddress,不管是AClass或是BClass, 这个时候不会想到要用SubNet,但若是上Internet那IPAddress 便弥足珍贵了,目前全球一阵Internet热,IPAddress已经愈 来愈少了,而所申请的IPAddress目前也趋保守,而且只有 经申请的IPAddress能在Internet使用,但对某些公司只能申 请到一个CCLass的IPAddress,但又有多个点需要使用,那这 时便需要使用到Subnet,这篇短文说明Subnet的原理及如 何规划。<br /><br />SubnetMask的介绍<br />设定任何网路上的任何设备不管是主机、PC、Router等 皆需要设定IPAddress,而跟随著IPAddress的是所谓的NetMask, 这个NetMask主要的目的是由IPAddress中也能获得NetworkNumber ,也就是说IPAddress和NetMask作AND而得到NetworkNumber,如下所 示<br /><br /><br />IPAddress 192.10.10.611000000.00001010.00001010.00000110 <br />NetMask 255.255.255.011111111.11111111.11111111.00000000 <br />AND ------------------------------------------------------------------- <br />etworkNumber 192.10.10.011000000.00001010.00001010.00000000 <br /><br />NetMask有所谓的预设值,如下所示<br /><br />ClassIPAddress范围NetMask<br />A　1.0.0.0-126.255.255.255255.0.0.0<br />B　128.0.0.0-191.255.255.255255.255.0.0<br />C　192.0.0.0-223.255.255.255255.255.255.0<br /><br />在预设的NetMask都只有255的值,在谈到SubnetMask时这个值 便不一定是255了。<br />在完整一组CClass中如203.67.10.0-203.67.10.255NetMask255.255.255.0, 203.67.10.0称之NetworkNumber(将IPAddress和Netmask作AND),而 203.67.10.255是Broadcast的IPAddress,所以这?两者皆不能使用,实 际只能使用203.67.10.1--203.67.10.254等254个IPAddress,这是以 255.255.255.0作NetMask的结果,而所谓SubnetMsk尚可将整组C Class分成数组NetworkNumber,这要在NEtMask作手脚,若是要将 整组CCLass分成2个NetworkNumber那NetMask设定为255.255.255.192, 若是要将整组CCLass分成8组NetworkNumber则NetMask要为 255.255.255.224,这是怎麽来的,由以上知道NetworkNumber是由IP Address和NetMask作AND而来的,而且将NetMask以二进位表示 法知道是1的会保留,而为0的去掉<br /><br /><br />192.10.10.193--11000000.00001010.00001010.10000001<br />255.255.255.0--11111111.11111111.11111111.00000000<br />--------------------------------------------------------------<br />192.10.10.0--11000000.00001010.00001010.00000000<br /><br /><br />以上是以255.255.255.0为NetMask的结果,NetworkNumber是192.10.10.0, 若是使用255.255.255.224作NetMask结果便有所不同<br /><br /><br />192.10.10.193--11000000.00001010.00001010.10000000<br />255.255.255.224--11111111.11111111.11111111.11100000<br />--------------------------------------------------------------<br />192.10.10.192--11000000.00001010.00001010.10000000<br /><br /><br />此时NetworkNumber变成了192.10.10.192,这便是Subnet。<br />那要如何决定所使用的NetMask,255.255.255.224以二进位表示 法为11111111.11111111.11111111.11100000,变化是在最後一组,11100000 便是224,以三个Bit可表示2的3次方便是8个NetworkNumber<br /><br />NetMask二进位表示法可分几个Network<br /><br />255.255.255.0 11111111.11111111.11111111.000000001 <br />255.255.255.128 11111111.11111111.11111111.100000002 <br />255.255.255.192 11111111.11111111.11111111.110000004 <br />255.255.255.224 11111111.11111111.11111111.111000008 <br />255.255.255.240 11111111.11111111.11111111.1111000016 <br />255.255.255.248 11111111.11111111.11111111.1111100032 <br />255.255.255.252 11111111.11111111.11111111.1111110064 <br /><br />以下使用255.255.255.224将C　Class203.67.10.0分成8组NetworkNumber,各 个NetworkNumber及其BroadcastIPAddress及可使用之IPAddress<br /><br />序号NetworkNumberBroadcast可使用之IPAddress<br /><br />1 203.67.10.0 203.67.10.31 203.67.10.1-203.67.10.30 <br />2 203.67.10.32 203.67.10.63 203.67.10.33-203.67.10.62 <br />3 203.67.10.64 203.67.10.95 203.67.10.65-203.67.10.94 <br />4 203.67.10.96 203.67.10.127 203.67.10.97-203.67.10.126 <br />5 203.67.10.128 203.67.10.159 203.67.10.129-203.67.10.158 <br />6 203.67.10.160 203.67.10.191 203.67.10.161-203.67.10.190 <br />7 203.67.10.192 203.67.10.223 203.67.10.193-203.67.10.222 <br />8 203.67.10.224 203.67.10.255 203.67.10.225-203.67.10.254 <br /><br /><br />可验证所使用的IPAddress是否如上表所示<br /><br />203.67.10.115--11001011.01000011.00001010.01110011<br />255.255.255.224--11111111.11111111.11111111.11100000<br />--------------------------------------------------------------<br />203.67.10.96--11001011.01000011.00001010.01100000<br /><br />203.67.10.55--11001011.01000011.00001010.00110111<br />255.255.255.224--11111111.11111111.11111111.11100000<br />--------------------------------------------------------------<br />203.67.10.32--11001011.01000011.00001010.00100000<br /><br />其他的NetMask所分成的NetworkNumber可自行以上述方法自行推演出来。<br /><br />Subnet的应用<br />使用Subnet是要解决只有一组CClass但需要数个NetworkNumber的问题,并不是解决IPAddress不够用的问题,因为使用 Subnet反而能使用的IPAddress会变少,Subnet通常是使用在总公司在台北,但分公司在台中,两者之间使用Router连线 ,同时也上Internet,但只申请到一组CCLassIPAddress,过Router又需不同的Network,所以此时就必须使用到Subnet,当然二 办公司间可以RemoteBridge连接,那便没有使用Subnet的问题,这点在此不讨论,所以在以上情况下的网路连线架 构及IPAddress的使用</div>
																								<div class="posttext">
																										<div class="posttext">（三）<br />TCP/IP（传输控制协议/ 网间协议）是一种网络通信协议，它规范了网络上的所有通信设备，尤其是一个主机与另一个主机之间的数据往来格式以及传送方式。 TCP/IP是INTERNET的基础协议，也是一种电脑数据打包和寻址的标准方法。在数据传送中，可以形象地理解为有两个信封，TCP和IP就像是信封，要传递的信息被划分成若干段，每一段塞入一个TCP信封，并在该信封面上记录有分段号的信息，再将TCP信封塞入IP大信封，发送上网。在接受端，一个TCP软件包收集信封，抽出数据，按发送前的顺序还原，并加以校验，若发现差错，TCP将会要求重发。因此，TCP/IP在INTERNET中几乎可以无差错地传送数据。<br /><br />在任何一个物理网络中, 各站点都有一个机器可识别的地址,该地址叫做物理地址. 物理地址有两个<br /><br />特点:<br /><br />物理地址的长度,格式等是物理网络技术的一部分, 物理网络不同,物理地址也不同. <br />同一类型不同网络上的站点可能拥有相同的物理地址. <br />以上两点决定了,不能用物理网络进行网间网通讯.<br /><br />在网络术语中，协议中，协议是为了在两台计算机之间交换数 据而预先规定的标准。TCP/IP并不是一个而是许多协 议，这就是为什么你经常听到它代表一个协议集的原因，而TCP和IP只是其中两个基本协议而已。<br /><br />你装在计算机-的TCP/IP软件提供了一个包括TCP、IP 以及TCP/IP协议集中其它协议的工具平台。特别是它包 括一些高层次的应用程序和FTP(文件传输协议)，它允 许用户在命令行上进行网络文件传输。<br /><br />TCP/IP是美国政府资助的高级研究计划署(ARPA)在二十世纪七十年代的一个研究成果，用来使全球的研究 网络联在一起形成一个虚拟网络，也就是国际互联 网。原始的<br /><br />Internet通过将已有的网络如ARPAnet转换到TCP/IP上来而形成，而这个Internet最终成为如今的国际互联网的骨干 网。<br /><br />如今TCP/IP如此重要的原因，在于它允许独立的网 格加入到Internet或组织在一起形成私有的内部网（Intranet）。 构成内部网的每个网络通过一种-做路由器或IP路由器 的设备在物理上联接在一起。路由器是一台用来从一 个网络到另一个网络传输数据包的计算机。在一个使 用TCP/IP的内部网中，信息通过使用一种独立的叫做IP 包（IPpacket）或IP数据报(IPdatagrams)的数据单元进--传输。TCP/IP 软件使得每台联到网络上的计算机同其它计算机“ 看”起来一模一样，事实上它隐藏了路由器和基本的网络 体系结构并使其各方面看起来都像一个大网。如同联 入以太网时需要确认一个48位的以太网地址一样，联入一个内部网也需要确认一个32位的IP地址。我们将它用带点的十进制数表示，如128.10.2.3。给定一个远程计算机的IP地址，在某个内部网或Internet上的本地计算 机就可以像处在同一个物理网络中的两台计算机那 样向远程计算机发送数据。<br /><br />TCP/IP提供了一个方案用来解决属于同一个内部网而分属不同物理网的两台计算机之间怎样交换数据的问题。这个方案包括许多部分，而TCP/IP协议集的 每个成员则用来解决问题的某一部分。如TCP/IP协议集 中最基本的协议-IP协议用来在内部网中交换数据并且 执行一项重要的功能：路由选择－－选择数据报从A主机到B主机将要经过的路径以及利用合适的路由器完成不同网络之间的跨越（hop）。<br /><br />TCP是一个更高层次的它允许运行在在不同主机上的应用程序相互交换数据流。TCP将数据流分成小段叫做TCP数据段（TCPsegments），并利用IP协议进行传输。在 大多数情况下，每个TCP数据段装在一个IP数据报中进 行发送。但如需要的话，TCP将把数据段分成多个数据报，而IP数据报则与同一网络不同主机间传输位流和 字节流的物理数据帧相容。由于IP并不能保证接收的 数据报的顺序相一致，TCP会在收信端装配TCP数据段并 形成一个不间断的数据流。FTP和Telnet就是两个非常流行的依靠TCP的TCP/IP应用程序。<br /><br />另一个重要的TCP/IP协议集的成员是用户数据报协议(UDP)，它同TCP相似但比TCP原始许多。TCP是一个可 靠的协议，因为它有错误检查和握手确认来保证数据 完整的到达目的地。UDP是一个“不可靠”的协议，因为 它不能保证数据报的接收顺序同发送顺序相同，甚至 不能保证它们是否全部到达。如果有可靠性要求，则 应用程序避免使用它。同许多TCP/IP工具同时提供的SNMP( 简单网络管理协议)就是一个使用UDP协议的应用例子。<br /><br />其它TCP/IP协议在TCP/IP网络中工作在幕后，但同样也发挥着重要作用。例如地址转换协议(ARP)将IP地址转换为物理网络地址如以太网地址。而与其对应的反向地址转换协议(RARP)做相反的工作，即将物理网络地址 转换为IP地址。网际控制报文协议(ICMP)则是一个支持性协议，它利用IP完成IP数据报在传输时的控制信息和 错误信息的传输。例如，如果一个路由器不能向前发送一个IP数据报，它就会利用ICMP来告诉发送者这里出 现了问题</div>
																										<div class="posttext">
																												<div class="posttext">TCP/IP基础（四）<br />网络设计者在解决网络体系结构时经常使用ISO/OSI（ 国际标准化组织/开放系统互连）七层模型，该模型每 一层代表一定层次的网络功能。最下面是物理层，它 代表着进行数据转输的物理介质，换句话说，即网络 电缆。其上是数据链路层，它通过网络接口卡提供服 务。最上层是应用层，这里运行着使用网络服务的应 用程序。<br /><br />TCP/IP是同ISO/OSI模型等价的。当一个数据单元 从网络应用程序下流到网络接口卡，它通过了一列的TCP/IP 模块。这其中的每一步，数据单元都会同网络另一端 对等TCP/IP模块所需的信息一起打成包。这样当数据最 终传到网卡时，它成了一个标准的以太帧(假设物理 网络是以太网)。而接收端的TCP/IP软件通过剥去以太网 帧并将数据向上传输过TCP/IP栈来为处于接收状态的应 用程序重新恢复原始数据(一种最好的了解TCP/IP工作实 质的方法，是使用探测程序来观察网络中的到处流动 的帧中被不同TCP/IP模块所加上的信息)。<br /><br />为了勾勒TCP/IP在现实网络世界中所扮演的角色， 请考虑当使用HTTP(超文本传输协议)的Web浏览器从连接 在Internet上的Web服务器上获取一页HTML数据时所发生的情 况。为形成同Web服务器的虚链路，浏览器使用一种被 抽象地称为套接口(socket)的高层软件。为了获 取Web页，它通过向套接口向套接口写入HTTPGET命令来向Web 服务器发出该指令。接下来套接口软件使用TCP协议向 Web服务器发出包含GET命令的字节流和位流，TCP将数据 分段并将各独立段传到IP模块，该模块将数据段转换 成数据报并发送给Web服务器。<br /><br />如果浏览器和服务器运--在不同物理网络的计 算机上(一般情况如此)，数据报从一个网络传到另一 个网络，直到抵达服务器所在的那个网。最终，数据 被传输到目的地址并被重新装配，这样Web服务器通过 读自己的套接口来获得数据主干，并进而查看连续的 数据流。对浏览器和服务器来说，数据在这一端写入 套接口而在另一端出现如同魔术一般，但这只是底 下发生的各种复杂的交互，它创造了数据经过网络无 缝传输的假象。<br /><br />这就是TCP/IP所做的：将许多小网联成一个大网。 并在这个大网也就是Internet上提供应用程序所需要的 相互通信的服务。<br /><br />评论：<br /><br />对于TCP/IP有许多可谈的，但这里仅讲三个关键 点：<br /><br />·TCP/IP是一族用来把不同的物理网络联在一 起构成网际网的协议。TCP/IP联接独立的网络形成一个 虚拟的网，在网内用来确认各种独立的不是物理网络 地址，而是IP地址。<br /><br />·TCP/IP使用多层体系结构，该结构清晰定义了 每个协议的责任。TCP和UDP向网络应用程序提供了高层 的数据传输服务，并都需要IP来传输数据包。IP有责任 为数据包到达目的地选择合适的路由。<br /><br />·在Internet主机上，两个运行着的应用程序之 间传送要通过主机的TCP/IP堆栈上下移动。在发送端TCP/IP 模块加在数据上的信息将在接收端对应的TCP/IP模块上 滤掉，并将最终恢复原始数据。<br /><br />如果你有兴趣学习更多的TCP/IP知识，这里有两个 较高层次的信息源RFC(RequestforComment)1180——叫做“TCP/IP Tutorial”的文档，你可以从许多普及的RFC的Internet节点上 下载。另一个是InternetworkingwithTCP/IP的第一卷：Principles，Protocols，and Architectures，作者DouglasE.Comer(1995，Prentice-Hall)。作为该系三部 曲中的第一部分，许多人把看成是一本TCP/IP圣经。<br />www.net130.com的Cisco教程/China-Pub电子书籍/栏目中有：<br />TCPIP详解 卷1协议 (http://www.net130.com/tutorial/chinapub/TCPIP详解%20卷1协议.zip) <br /><br />TCPIP详解 卷2实现 (http://www.net130.com/tutorial/chinapub/TCPIP详解%20卷2实现.zip) <br /><br />TCPIP详解 卷3事务协议 (<a href="http://www.net130.com/tutorial/chinapub/TCPIP"><font color="#000080">http://www.net130.com/tutorial/chinapub/TCPIP</font></a>详解%20卷3.zip)</div>
																												<div class="posttext">
																												</div>
																												<div class="posttext">--------------</div>
																												<div class="posttext">在 二，子网设定里。<br />192.10.10.193--11000000.00001010.00001010.10000001 应为11000001<br />255.255.255.0--11111111.11111111.11111111.00000000<br /><br /><br /><br />192.10.10.193--11000000.00001010.00001010.10000000 应为11000001<br />255.255.255.224--11111111.11111111.11111111.11100000<br />--------------------------------------------------------------<br />192.10.10.192--11000000.00001010.00001010.10000000 应为11000000<br /></div>
																										</div>
																								</div>
																						</div>
																				</div>
																		</div>
																</div>
														</div>
												</div>
										</div>
								</div>
						</div>
				</div>
		</div>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/21739.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-01-10 22:26 <a href="http://www.cnitblog.com/zouzheng/articles/21739.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>对const声明变量的奇异行为的探讨</title><link>http://www.cnitblog.com/zouzheng/articles/21738.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Wed, 10 Jan 2007 14:20:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/21738.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/21738.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/21738.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/21738.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/21738.html</trackback:ping><description><![CDATA[
		<div class="postText">
				<span id="ArticleContent1_ArticleContent1_lblContent">
						<div style="MARGIN: 0cm 12pt 0pt 13.5pt; TEXT-ALIGN: left" align="left">
								<font size="3">The information in this article applies to:</font>
						</div>
						<div style="MARGIN: 0cm 12pt 0pt 13.5pt; TEXT-ALIGN: left" align="left">
								<span style="FONT-SIZE: 10pt">- C/C++</span>
						</div>
						<div style="MARGIN: 0cm 12pt 0pt 13.5pt; TEXT-ALIGN: left" align="left">
								<font size="3">----------------------------------------------------------------</font>
						</div>
						<div style="MARGIN: 13pt 0cm">
								<strong>
										<font size="5">
												<a name="OLE_LINK2">
														<span style="COLOR: black">奇异的现象：</span>
												</a>
										</font>
								</strong>
						</div>
						<div style="TEXT-INDENT: 21pt">
								<font size="3">我把这个试验的源代码列出来：</font>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">int main(int argc, char* argv[])</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">{</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">       const int x=10000;</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">       int *y=0;</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">       y=(int*)&amp;x;</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">       *y=10;</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">       printf("%d\n", x);</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">       printf("%d\n", *y);</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">       return 0;</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">}</font>
								</span>
						</div>
						<div style="TEXT-INDENT: 21pt">
								<font size="3">首先我们声明了一个const变量x，初始化为10000。然后让一个int指针y指向x。通过给*y赋值，从而改变了x的实际值！</font>
						</div>
						<div style="TEXT-INDENT: 21pt">
								<font size="3">
										<strong>虽然在</strong>
										<strong>Watch</strong>
										<strong>窗口中你明明看到</strong>
										<strong>x</strong>
										<strong>的值确实是</strong>
										<strong>10</strong>
										<strong>，但是</strong>
										<strong>printf</strong>
										<strong>出来的</strong>
										<strong>x</strong>
										<strong>的值却偏偏是</strong>
										<strong>10000</strong>
										<strong>！！</strong>
								</font>
						</div>
						<div style="TEXT-INDENT: 21pt">
								<font size="3">可是，这个已经被彻底抹去的10000，又是从哪里被找回来的呢？</font>
						</div>
						<div>
								<font size="3">
								</font> </div>
						<div style="MARGIN: 13pt 0cm">
								<strong>
										<font size="5">
												<span style="COLOR: black">我的解释：</span>
										</font>
								</strong>
						</div>
						<div style="TEXT-INDENT: 21pt">
								<font size="3">这样的代码经过VC编译器的Debug版本的编译，最后生成的完整的汇编代码为(我做了注释，可以参考一下)：</font>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">11:   int main(int argc, char* argv[])</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">12:   {</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">00401250   push        ebp</font>
								</span>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">第一步，将</span>
										<span style="COLOR: red">基址寄存器</span>
										<span style="COLOR: red">(EBP) </span>
										<span style="COLOR: red">压入堆栈</span>
								</font>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">00401251   mov         ebp,esp          </font>
								</span>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">第二步，</span>
										<span style="COLOR: red">把当前的栈顶指针</span>
										<span style="COLOR: red">(ESP)</span>
										<span style="COLOR: red">拷贝到</span>
										<span style="COLOR: red">EBP</span>
										<span style="COLOR: red">，做为新的基地址</span>
								</font>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">00401253   sub         esp,48h</font>
								</span>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">第三步，</span>
										<span style="COLOR: red">把</span>
										<span style="COLOR: red">ESP</span>
										<span style="COLOR: red">减去一个数值，用来为本地变量留出一定空间。这里减去</span>
										<span style="COLOR: red">48h</span>
										<span style="COLOR: red">，也就是</span>
								</font>
						</div>
						<div>
								<span style="COLOR: red">
										<font size="3">\\ 72 .</font>
								</span>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">这里对前面的三步说明一下：</span>
										<span style="COLOR: red">ESP</span>
										<span style="COLOR: red">和</span>
										<span style="COLOR: red">EBP</span>
										<span style="COLOR: red">寄存器是堆栈专用的。堆栈基址指针</span>
										<span style="COLOR: red">(EBP)</span>
										<span style="COLOR: red">寄</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">存器确定堆栈帧的起始位置，而堆栈指针</span>
										<span style="COLOR: red">(ESP)</span>
										<span style="COLOR: red">寄存器执行当前堆栈顶。在函数的入口处，</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">当前堆栈基址指针被压到了堆栈中，并且当前堆栈指针成为新的堆栈基址指针。局部变</span>
										<span style="COLOR: red"> </span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">量的存储空间、函数使用的各种需要保存的寄存器的存储空间在函数入口处也被预留出</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">来。</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">所以也就有了下面的三个压栈行为。</span>
								</font>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">                     </font>
								</span>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">下面是连续三个压栈，</span>
										<span style="COLOR: red">第</span>
										<span style="COLOR: red">4</span>
										<span style="COLOR: red">步：</span>
								</font>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">00401256   push        ebx</font>
								</span>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">将</span>
										<span style="COLOR: red">ebx</span>
										<span style="COLOR: red">寄存器压栈；</span>
										<span style="COLOR: red">EBX</span>
										<span style="COLOR: red">寄存器是段寄存器的一种，为基址</span>
										<span style="COLOR: red"> DS </span>
										<span style="COLOR: red">数据段；</span>
								</font>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">00401257   push        esi</font>
								</span>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">将</span>
										<span style="COLOR: red">esi</span>
										<span style="COLOR: red">寄存器压栈；</span>
										<span style="COLOR: red">ESI</span>
										<span style="COLOR: red">寄存器是指针寄存器的一种。是内存移动和比较操作的源地址寄</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">存器；</span>
								</font>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">00401258   push        edi</font>
								</span>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">将</span>
										<span style="COLOR: red">edi</span>
										<span style="COLOR: red">寄存器压栈；</span>
										<span style="COLOR: red">EDI</span>
										<span style="COLOR: red">寄存器是指针寄存器的一种。是内存移动和比较操作的目标地址</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">寄存器</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">以上四步执行完之后，函数入口处的堆栈帧结构如下所示：</span>
								</font>
						</div>
						<div> </div>
						<div> </div>
						<div> </div>
						<div> </div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">值得注意的是，上面所说的对于</span>
										<span style="COLOR: red">Debug</span>
										<span style="COLOR: red">版本才是正确的，对于</span>
										<span style="COLOR: red">Release</span>
										<span style="COLOR: red">版本可不一定对。</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ Release </span>
										<span style="COLOR: red">版本也许已经把堆栈基址指针优化掉了。</span>
								</font>
						</div>
						<div>
								<span style="COLOR: red">
										<font size="3">
										</font>
								</span> </div>
						<div>
								<span style="COLOR: blue">
										<font size="3">00401259   lea         edi,[ebp-48h]</font>
								</span>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">第</span>
										<span style="COLOR: red">5</span>
										<span style="COLOR: red">步，</span>
										<span style="COLOR: red">lea</span>
										<span style="COLOR: red">指令装入有效地址，用来得到局部变量和函数参数的指针。这里</span>
										<span style="COLOR: red">[ebp-48h]</span>
										<span style="COLOR: red">就是基地址再向下偏移</span>
										<span style="COLOR: red">48h</span>
										<span style="COLOR: red">，就是前面说的为本地变量留出的空间的起始地址；将这个值装载入</span>
										<span style="COLOR: red">edi</span>
										<span style="COLOR: red">寄存器，从而得到局部变量的地址；</span>
								</font>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">
										</font>
								</span> </div>
						<div>
								<font size="3">
										<strong>
												<span style="COLOR: purple">\\ </span>
										</strong>
										<strong>
												<span style="COLOR: purple">下面的这第六步可是非常的重要，请记住：</span>
										</strong>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">第六步，给段寄存器预先赋值：</span>
								</font>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">0040125C   mov         ecx,12h</font>
								</span>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ ECX</span>
										<span style="COLOR: red">寄存器是段寄存器的一种，为计数器</span>
										<span style="COLOR: red"> SS </span>
										<span style="COLOR: red">堆栈段。设为</span>
										<span style="COLOR: red">12h</span>
										<span style="COLOR: red">。</span>
								</font>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">00401261   mov         eax,0CCCCCCCCh</font>
								</span>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ EAX</span>
										<span style="COLOR: red">寄存器是段寄存器的一种，为累加器</span>
										<span style="COLOR: red"> CS </span>
										<span style="COLOR: red">代码段；设为</span>
										<span style="COLOR: red">0CCCCCCCCh</span>
										<span style="COLOR: red">。</span>
								</font>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">
										</font>
								</span> </div>
						<div>
								<span style="COLOR: blue">
										<font size="3">00401266   rep stos    dword ptr [edi]</font>
								</span>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">这句话是干吗的？</span>
								</font>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">
										</font>
								</span> </div>
						<div>
								<font size="3">
										<strong>
												<span style="COLOR: purple">\\ </span>
										</strong>
										<strong>
												<span style="COLOR: purple">下面开始我们的代码了：</span>
										</strong>
								</font>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">13:       const int x=10000;</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">00401268   mov         dword ptr [ebp-4], 2710h</font>
								</span>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">第一步，在基地址向下偏移</span>
										<span style="COLOR: red">4</span>
										<span style="COLOR: red">个字节所指向的地址，将</span>
										<span style="COLOR: red">10000</span>
										<span style="COLOR: red">这个</span>
										<span style="COLOR: red">DWORD</span>
										<span style="COLOR: red">数值放进\\去；</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">可以看出的是，对于一个普通的</span>
										<span style="COLOR: red">int z = 10000;</span>
										<span style="COLOR: red">汇编代码依然是这个样子。说明从这句</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">
										</span>
								</font>
								<font size="3">
										<span style="COLOR: red">\\ 话</span>
										<span style="COLOR: red">是无法分清楚局部</span>
										<span style="COLOR: red">const</span>
										<span style="COLOR: red">变量的初始化和普通变量的初始化的！这一点很重要！就</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">
										</span>
								</font>
								<font size="3">
										<span style="COLOR: red">\\ 是说编译器</span>
										<span style="COLOR: red">从表面上是无法分清楚一个局部</span>
										<span style="COLOR: red">const</span>
										<span style="COLOR: red">变量和一个普通变量的。</span>
								</font>
						</div>
						<div>
								<span style="COLOR: red">
										<font size="3">
										</font>
								</span> </div>
						<div>
								<span style="COLOR: blue">
										<font size="3">14:       int *y=0;</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">0040126F   mov         dword ptr [ebp-8],0</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">
										</font>
								</span> </div>
						<div>
								<span style="COLOR: blue">
										<font size="3">15:       y=(int*)&amp;x;</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">00401276   lea         eax,[ebp-4]</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">00401279   mov         dword ptr [ebp-8],eax</font>
								</span>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">第</span>
										<span style="COLOR: red">2</span>
										<span style="COLOR: red">步，将</span>
										<span style="COLOR: red">x</span>
										<span style="COLOR: red">的地址装载到</span>
										<span style="COLOR: red">EAX</span>
										<span style="COLOR: red">寄存器；</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">第</span>
										<span style="COLOR: red">3</span>
										<span style="COLOR: red">步，再把这个地址作为一个数值导到</span>
										<span style="COLOR: red">y</span>
										<span style="COLOR: red">的地址，这样</span>
										<span style="COLOR: red">y</span>
										<span style="COLOR: red">就指向了</span>
										<span style="COLOR: red">x</span>
										<span style="COLOR: red">！</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">这是局部</span>
										<span style="COLOR: red">const</span>
										<span style="COLOR: red">变量声明的情况！</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">而对于全局</span>
										<span style="COLOR: red">const</span>
										<span style="COLOR: red">变量声明的情况，这句</span>
										<span style="COLOR: red">y=(int*)&amp;x;</span>
										<span style="COLOR: red">的汇编却是：</span>
								</font>
						</div>
						<div>
								<span style="COLOR: red">
										<font size="3">\\ 00401276   mov         dword ptr [ebp-8],offset x (0043101c)</font>
								</span>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">一个很显著的区别！</span>
								</font>
						</div>
						<div>
								<span style="COLOR: red">
										<font size="3">
										</font>
								</span> </div>
						<div>
								<span style="COLOR: blue">
										<font size="3">16:       *y=10;</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">0040127C   mov         ecx,dword ptr [ebp-8]</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">0040127F   mov         dword ptr [ecx],0Ah</font>
								</span>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">第</span>
										<span style="COLOR: red">4</span>
										<span style="COLOR: red">步，通过</span>
										<span style="COLOR: red">ECX</span>
										<span style="COLOR: red">寄存器倒手，将</span>
										<span style="COLOR: red">y</span>
										<span style="COLOR: red">所指向的地址的数值修改为</span>
										<span style="COLOR: red">0Ah</span>
										<span style="COLOR: red">，也就是</span>
										<span style="COLOR: red">10</span>
										<span style="COLOR: red">！</span>
								</font>
						</div>
						<div>
								<font size="3">
										<strong>
												<span style="COLOR: purple">\\ </span>
										</strong>
										<strong>
												<span style="COLOR: purple">编译器之所以允许这种修改</span>
										</strong>
										<strong>
												<span style="COLOR: purple">const</span>
										</strong>
										<strong>
												<span style="COLOR: purple">变量值的非法情况，是因为编译器并不知道这\\是一个</span>
										</strong>
								</font>
						</div>
						<div>
								<font size="3">
										<strong>
												<span style="COLOR: purple">\\ const</span>
										</strong>
										<strong>
												<span style="COLOR: purple">变量，它实在是和普通的变量太像了！</span>
										</strong>
								</font>
						</div>
						<div>
								<span style="COLOR: red">
										<font size="3">
										</font>
								</span> </div>
						<div>
								<span style="COLOR: blue">
										<font size="3">17:</font>
								</span>
						</div>
						<div>
								<strong>
										<span style="COLOR: blue">
												<font size="3">18:       printf("%d\n", x);</font>
										</span>
								</strong>
						</div>
						<div>
								<strong>
										<span style="COLOR: blue">
												<font size="3">00401285   push        2710h</font>
										</span>
								</strong>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">第</span>
										<span style="COLOR: red">5</span>
										<span style="COLOR: red">步，将</span>
										<span style="COLOR: red">10000</span>
										<span style="COLOR: red">数值压栈！按照惯例，这个</span>
										<span style="COLOR: red">2710h</span>
										<span style="COLOR: red">会被存在当前栈顶指针前</span>
										<span style="COLOR: red">4</span>
										<span style="COLOR: red">个字节</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">处。原来</span>
										<span style="COLOR: red">ESP</span>
										<span style="COLOR: red">指向</span>
										<span style="COLOR: red">0012FF2C</span>
										<span style="COLOR: red">，所以现在指向</span>
										<span style="COLOR: red">0012FF28</span>
										<span style="COLOR: red">了。</span>
								</font>
						</div>
						<div>
								<font size="3">
										<strong>
												<span style="COLOR: purple">\\ </span>
										</strong>
										<strong>
												<span style="COLOR: purple">编译器为什么会直接</span>
										</strong>
										<strong>
												<span style="COLOR: purple">push</span>
										</strong>
										<strong>
												<span style="COLOR: purple">一个常量入栈呢？</span>
										</strong>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">我觉得可能是这样：制定</span>
										<span style="COLOR: red">C++</span>
										<span style="COLOR: red">编译器规则的人想反正都是</span>
										<span style="COLOR: red">const</span>
										<span style="COLOR: red">变量了，它的值肯定不</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">能变。</span>
										<span style="COLOR: red">printf</span>
										<span style="COLOR: red">一个普通变量是倒手两个寄存器后把</span>
										<span style="COLOR: red">EAX</span>
										<span style="COLOR: red">寄存器的内容压栈，多影响效率</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">还不如直接将这个</span>
										<span style="COLOR: red">const</span>
										<span style="COLOR: red">变量的值压栈呢。</span>
								</font>
						</div>
						<div>
								<span style="COLOR: red">
										<font size="3">
										</font>
								</span> </div>
						<div>
								<span style="COLOR: blue">
										<font size="3">0040128A   push        offset string "%d\n" (0042f01c)</font>
								</span>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">再把格式化压栈；</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">这样，</span>
										<span style="COLOR: red">printf</span>
										<span style="COLOR: red">函数将取栈顶的内容打印，当然是按照</span>
										<span style="COLOR: red">%d\n</span>
										<span style="COLOR: red">来打印的，所以只会再取栈顶</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ 0x</span>
										<span style="COLOR: red">0012FF28</span>
										<span style="COLOR: red">指向的内容</span>
										<span style="COLOR: red">；所以打印出来的就是上面压栈的常量</span>
										<span style="COLOR: red">2710h</span>
										<span style="COLOR: red">！</span>
								</font>
						</div>
						<div>
								<font size="3">
										<span style="COLOR: red">\\ </span>
										<span style="COLOR: red">这就是我给出的解释。请高手们指正。</span>
								</font>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">
										</font>
								</span> </div>
						<div>
								<span style="COLOR: blue">
										<font size="3">0040128F   call        printf (004082f0)</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">00401294   add         esp,8</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">19:</font>
								</span>
						</div>
						<div>
								<span style="COLOR: blue">
										<font size="3">20:       printf("%d\n", *y);</font>
								</span>
						</div>
						<div>
								<strong>
										<span style="COLOR: blue">
												<font size="3">00401297   mov         edx,dword ptr [ebp-8]</font>
										</span>
								</strong>
						</div>
						<div>
								<strong>
										<span style="COLOR: blue">
												<font size="3">0040129A   mov         eax,dword ptr [edx]</font>
										</span>
								</strong>
						</div>
						<div>
								<strong>
										<span style="COLOR: blue">
												<font size="3">0040129C   push        eax</font>
										</span>
								</strong>
						</div>
						<div>
								<font size="3">
										<strong>
												<span style="COLOR: red">\\ </span>
										</strong>
										<strong>
												<span style="COLOR: red">看，对于一个普通变量的</span>
										</strong>
										<strong>
												<span style="COLOR: red">printf</span>
										</strong>
								</font>
						</div>
				</span>
		</div>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/21738.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-01-10 22:20 <a href="http://www.cnitblog.com/zouzheng/articles/21738.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【摘】蓝牙技术及其协议栈 </title><link>http://www.cnitblog.com/zouzheng/articles/18577.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Sun, 29 Oct 2006 03:01:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/18577.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/18577.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/18577.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/18577.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/18577.html</trackback:ping><description><![CDATA[
		<span style="FONT-SIZE: 14pt; mso-ascii-font-family: Times New Roman; mso-hansi-font-family: Times New Roman; mso-bidi-font-size: 10.0pt">
				<font face="Arial" color="#000000">蓝牙技术及其协议栈</font>
		</span>
		<span lang="EN-AU" style="FONT-SIZE: 14pt; mso-bidi-font-size: 10.0pt">
				<font color="#0000ff">
				</font> <br /></span>
		<span lang="EN-AU" style="mso-tab-count: 1">        </span>
		<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">蓝牙</span>
		<span lang="EN-AU">(Bluetooth)</span>
		<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">计划是由</span>
		<span lang="EN-AU">Ericsson</span>
		<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">、</span>
		<span lang="EN-AU">IBM</span>
		<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">、</span>
		<span lang="EN-AU">Intel</span>
		<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">、</span>
		<span lang="EN-AU">Nokia</span>
		<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">和</span>
		<span lang="EN-AU">Toshiba</span>
		<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">等公司在</span>
		<span lang="EN-AU">1998</span>
		<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">年联合推出的一项先进的无线网络技术。遵循蓝牙协议的各类数据及语音设备将能够用微波取代传统网络中错综复杂的电缆，非常方便地实现快速、灵活、安全、低代价、低功耗的数据和语音通信。由于蓝牙技术将在人们的日常生活和工作中扮演重要角色，市场潜力巨大，因此，它将成为</span>
		<span lang="EN-AU">21</span>
		<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">世纪的投资热点。文章将重点介绍蓝牙技术的工作原理和蓝牙协议栈内容。分为四个部分：术语、</span>
		<span style="FONT-FAMILY: 宋体; mso-hansi-font-family: 'Times New Roman'">蓝牙技术工作原理、</span>
		<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">蓝牙硬件模块的组成、蓝牙软件协议栈的介绍。</span>
		<span lang="EN-AU">
				<br />
		</span>
		<span lang="EN-AU">1．<span style="FONT: 7pt 'Times New Roman'"></span></span>
		<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">术语</span>
		<span lang="EN-AU">
				<br />
		</span>
		<span lang="EN-AU">* </span>
		<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">微微网（</span>
		<span lang="EN-AU">Piconet</span>
		<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）：是由采用蓝牙技术的设备以特定方式组成的网络。</span>
		<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">微微网的建立是由两台设备（如便携式电脑和蜂窝电话）的连接开始，最多由</span>
		<span lang="EN-AU">8</span>
		<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">台设备构成。所有的蓝牙设备都是对等的，以同样的方式工作。然而，当一个微微网建立时，只有一台为主设备，其他均为从设备，而且在一个微微网存在期间将一直维持这一状况。</span>
		<p class="MsoNormal" style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; MARGIN-LEFT: 36pt; LINE-HEIGHT: 150%">
				<span lang="EN-AU">* </span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">分布式网络（</span>
				<span lang="EN-AU">Scatternet</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）：是由多个独立、非同步的微微网形成的。</span>
		</p>
		<p class="MsoNormal" style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; MARGIN-LEFT: 36pt; LINE-HEIGHT: 150%">
				<span lang="EN-AU">* </span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">主设备（</span>
				<span lang="EN-AU">Master unit</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）</span>
				<span lang="EN-AU">:</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">在微微网中，如果某台设备的时钟和跳频序列用于同步其他设备，则称它为主设备。</span>
		</p>
		<p class="MsoNormal" style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; TEXT-INDENT: 36pt; LINE-HEIGHT: 150%">
				<span lang="EN-AU">* </span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">从设备（</span>
				<span lang="EN-AU">Slave unit</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）：非主设备的设备均为从设备。</span>
		</p>
		<p class="MsoNormal" style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; TEXT-INDENT: 36pt; LINE-HEIGHT: 150%">
				<span lang="EN-AU">* MAC</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">地址</span>
				<span lang="EN-AU">(MAC address)</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">：用</span>
				<span lang="EN-AU">3</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">比特表示的地址，用于区分微微网中的设备。</span>
		</p>
		<p class="MsoNormal" style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; MARGIN-LEFT: 36pt; LINE-HEIGHT: 150%">
				<span lang="EN-AU">* </span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">休眠设备（</span>
				<span lang="EN-AU">Parked units</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）：在微微网中只参与同步，但没有</span>
				<span lang="EN-AU">MAC</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">地址的设备。</span>
		</p>
		<p class="MsoNormal" style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; MARGIN-LEFT: 36pt; LINE-HEIGHT: 150%">
				<span lang="EN-AU">* </span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">监听及保持方式（</span>
				<span lang="EN-AU">Sniff and Hold mode</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）：指微微网中从设备的两种低功耗工作方式。</span>
		</p>
		<p class="MsoNormal" style="MARGIN-LEFT: 36pt">
				<span lang="EN-AU"> </span>
				<span lang="EN-AU" style="FONT-FAMILY: 宋体; mso-hansi-font-family: 'Times New Roman'">2．<span style="FONT: 7pt 'Times New Roman'"></span></span>
				<span style="FONT-FAMILY: 宋体; mso-hansi-font-family: 'Times New Roman'">蓝牙技术工作原理<span lang="EN-AU"><p></p></span></span>    <span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">其基本实现原理为蓝牙设备依靠专用的蓝牙微芯片能使设备在短距离范围内发送无线电信号，来寻找另一个蓝芽设备。一旦找到，相互之间便开始通信，交换信息。</span><span lang="EN-AU"></span></p>
		<p>
				<span lang="EN-AU"> 1）<span style="FONT: 7pt 'Times New Roman'"></span></span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">蓝牙的无线通信技术特征</span>
				<span lang="EN-AU">
				</span>
		</p>
		<p>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">蓝牙的无线通信技术采用每秒</span>
				<span lang="EN-AU">1600</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">次的快跳频和短分组技术，减少干扰和信号衰落，保证传输的可靠性；</span>
				<span lang="EN-AU">
				</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">以时分方式进行全双工通信，传输速率设计为</span>
				<span lang="EN-AU">1MHz</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">；</span>
				<span lang="EN-AU">
				</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">采用前向纠错</span>
				<span lang="EN-AU">(FEC)</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">编码技术，减少远距离传输时的随机噪声影响；</span>
				<span lang="EN-AU">
				</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">其工作频段为非授权的工业、医学、科学频段（</span>
				<span lang="EN-AU">2.4GHz</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的</span>
				<span lang="EN-AU">ISM</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">频段），保证能在全球范围内使用这种无线通用接口和通信技术；</span>
				<span lang="EN-AU">
				</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">话音采用抗衰落能力很强的连续可变斜率调制</span>
				<span lang="EN-AU">(CVSD)</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">编码方式以提高话音质量；</span>
				<span lang="EN-AU">
				</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">采用频率调制方式，降低设备的复杂性。</span>
				<span lang="EN-AU">
						<br />
				</span>
				<span lang="EN-AU">2</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）拓扑结构</span>
				<span lang="EN-AU">
						<br />
				</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">蓝牙系统支持点对点以及点对多点通信。几个相互独立、以特定方式连接在一起的微微网构成分布式网络，各微微网由不同的跳频序列来区分。在同一微微网中，所有的用户均用同一跳频序列同步。拓扑结构如图</span>
				<span lang="EN-AU">1</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">所示。</span>
				<span lang="EN-AU">
				</span>
		</p>
		<p class="MsoNormal" style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 150%">
				<wrapblock>
				</wrapblock>
				<shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600">
				</shapetype>
				<stroke joinstyle="miter">
				</stroke>
				<formulas>
				</formulas>
				<f eqn="if lineDrawn pixelLineWidth 0">
				</f>
				<f eqn="sum @0 1 0">
				</f>
				<f eqn="sum 0 0 @1">
				</f>
				<f eqn="prod @2 1 2">
				</f>
				<f eqn="prod @3 21600 pixelWidth">
				</f>
				<f eqn="prod @3 21600 pixelHeight">
				</f>
				<f eqn="sum @0 0 1">
				</f>
				<f eqn="prod @6 1 2">
				</f>
				<f eqn="prod @7 21600 pixelWidth">
				</f>
				<f eqn="sum @8 21600 0">
				</f>
				<f eqn="prod @7 21600 pixelHeight">
				</f>
				<f eqn="sum @10 21600 0">
				</f>
				<path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f">
				</path>
				<lock aspectratio="t" v:ext="edit">
				</lock>
				<shape id="_x0000_s1026" style="MARGIN-TOP: 25.3pt; Z-INDEX: 1; LEFT: 0px; MARGIN-LEFT: 31.05pt; WIDTH: 329.9pt; POSITION: absolute; HEIGHT: 162pt; TEXT-ALIGN: left; mso-position-horizontal-relative: text; mso-position-vertical-relative: text" o:allowincell="f" type="#_x0000_t75">
				</shape>
				<imagedata o:title="" src="http://www.imag.com.cn/embedded/file:///C:/WINNT/Profiles/ADMINI~1/LOCALS~1/Temp/msoclip1/01/clip_image001.wmz">
				</imagedata>
				<textbox style="mso-next-textbox: #_x0000_s1026">
				</textbox>
				<wrap type="topAndBottom">
				</wrap>
				<span style="mso-ignore: vglayout">
						<table cellspacing="0" cellpadding="0" align="left">
								<tbody>
										<tr>
												<td width="41"> </td>
										</tr>
										<tr>
												<td> </td>
												<td>
														<img height="216" alt="" src="http://www.imag.com.cn/images/blueto1.gif" width="440" v:shapes="_x0000_s1026" />
												</td>
										</tr>
								</tbody>
						</table>
				</span>
		</p>
		<p align="center">
				<br style="mso-ignore: vglayout" clear="all" />
				<span lang="EN-AU"> </span>
		</p>
		<p> </p>
		<p align="center">
				<span lang="EN-AU"> </span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">图</span>
				<span lang="EN-AU">1 </span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">分布式网络</span>
		</p>
		<p class="MsoNormal" style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; LINE-HEIGHT: 150%">
				<span lang="EN-AU"> <span style="mso-tab-count: 1">         </span>3</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）连接建立的过程及其工作状态的转换</span>
				<span lang="EN-AU">
						<br />
				</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">在微微网建立之前，所有设备都处于就绪</span>
				<span lang="EN-AU">(STANDBY)</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">状态。在该状态下，未连接的设备每隔</span>
				<span lang="EN-AU">1.28</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">秒监听一次消息，设备一旦被唤醒，就在预先设定的</span>
				<span lang="EN-AU">32</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">个跳频频率上监听信息。跳频数目因地区而异，但</span>
				<span lang="EN-AU">32</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">个跳频频率为绝大多数国家所采用。</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">连接进程由主设备初始化。如果一个设备的地址已知，就采用寻呼消息</span>
				<span lang="EN-AU">(Page message)</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">建立连接；如果地址未知，就采用紧随寻呼消息的查询消息</span>
				<span lang="EN-AU">(Inquiry message)</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">建立连接。查询消息主要用来查询地址未知的设备</span>
				<span lang="EN-AU">(</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">如公用打印机、传真机等</span>
				<span lang="EN-AU">)</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，它与寻呼消息类似，但需要附加一个周期来收集所有的应答。在寻呼状态</span>
				<span lang="EN-AU">(PAGE state)</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，主设备在</span>
				<span lang="EN-AU">16</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">个跳频频率上发送一串相同的页信息给从设备，如果没有收到应答，主设备就在另外的</span>
				<span lang="EN-AU">16</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">个跳频频率上发送寻呼消息。主设备到从设备的最大时延为两个唤醒周期（</span>
				<span lang="EN-AU">2.56</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">秒），平均时延为半个唤醒周期（</span>
				<span lang="EN-AU">0.64</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">秒）。</span>
				<span lang="EN-AU">
						<br /> </span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">在微微网中，无数据传输的设备转入节能工作状态。主设备可将从设备设置为保持方式</span>
				<span lang="EN-AU">(HOLD mode)</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，此时，只有内部定时器工作；从设备也可以要求转入保持方式。设备由保持方式转出后，可以立即恢复数据传输。连接几个微微网或管理低功耗器件（如温度传感器）时，常使用保持方式。监听方式</span>
				<span lang="EN-AU">(SNIFF mode)</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">和休眠方式（</span>
				<span lang="EN-AU">PARK mode</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）是另外两种低功耗工作方式。在监听方式下，从设备监听网络的时间间隔增大，其间隔大小视应用情况由编程确定；在休眠方式下，设备放弃了</span>
				<span lang="EN-AU">MAC</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">地址，仅偶尔监听网络同步信息和检查广播信息。各节能方式依电源效率高低排列为：休眠方式→保持方式→监听方式。</span>
				<span lang="EN-AU">
						<br />
				</span>
				<span lang="EN-AU">4</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）蓝牙对语音和数据的支持</span>
				<span lang="EN-AU">
						<br />  </span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">为了保证各种使用场合的应用，蓝牙的基带协议是电路交换和分组交换的组合，可以同时支持语音和数据的传输。该技术支持两种连接方式：面向连接（</span>
				<span lang="EN-AU">SCO</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）方式，主要用于话音传输；无连接</span>
				<span lang="EN-AU">(ACL)</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">方式，主要用于分组数据传输。</span>
		</p>
		<p class="MsoNormal" style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; MARGIN-LEFT: 36pt; TEXT-INDENT: 36pt; LINE-HEIGHT: 150%">
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">在同一微微网中，不同的主从设备可以采用不同的连接方式，在一次通信中，连接方式可以任意改变。每一连接方式可支持</span>
				<span lang="EN-AU">16</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">种不同的分组类型，其中控制分组有</span>
				<span lang="EN-AU">4</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">种，是</span>
				<span lang="EN-AU">SCO</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">和</span>
				<span lang="EN-AU">ACL</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">通用的分组，两种连接方式均采用时分双工</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">（</span>
				<span lang="EN-AU">TDD</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）通信。</span>
				<span lang="EN-AU">SCO</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">为对称连接，每一个话音通道支持</span>
				<span lang="EN-AU">64kbps</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的同步话音，支持限时话音传送，主从设备无需轮询即可发送数据。</span>
				<span lang="EN-AU">SCO</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的分组既可以是话音又可以是数据，当发生中断时，只有数据部分需要重传。</span>
				<span lang="EN-AU">ACL</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">是面向分组的连接，它支持对称和非对称两种传输流量：</span>
				<span lang="EN-AU">ACL</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的非对称连接支持正向速率</span>
				<span lang="EN-AU">721kbps</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">、反向应答速率为</span>
				<span lang="EN-AU">57.6kbps</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的；对称连接速率为</span>
				<span lang="EN-AU">432.6kbps</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">。</span>
				<span lang="EN-AU">ACL</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">也支持通过广播方式发送信息。在</span>
				<span lang="EN-AU">ACL</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">方式下，主设备控制链路带宽，负责从设备带宽的分配；从设备依轮询发送数据。</span>
		</p>
		<p class="MsoNormal" style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; MARGIN-LEFT: 36pt; TEXT-INDENT: 36pt; LINE-HEIGHT: 150%">
				<span lang="EN-AU">
						<span style="mso-spacerun: yes"> </span>  </span>
				<span lang="EN-AU">    3</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">、蓝牙硬件模块的组成</span>
				<span lang="EN-AU">
						<p>
						</p>
				</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">   蓝牙模块包括以下两个部分：</span>
		</p>
		<p class="MsoNormal" style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; MARGIN-LEFT: 36pt; TEXT-INDENT: 36pt; LINE-HEIGHT: 150%">
				<span lang="EN-AU">1.</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">无线射频（</span>
				<span lang="EN-AU">RF</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）单元</span>
				<span lang="EN-AU">
						<br />           </span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">蓝牙系统的天线发射功率符合</span>
				<span lang="EN-AU">FCC</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">关于</span>
				<span lang="EN-AU">ISM</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">波段的要求。系统的最大跳频速率为</span>
				<span lang="EN-AU">1600</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">跳</span>
				<span lang="EN-AU">/</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">秒，在</span>
				<span lang="EN-AU">2.402GHz</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">到</span>
				<span lang="EN-AU">2.480GHz</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">之间，采用</span>
				<span lang="EN-AU">79</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">个</span>
				<span lang="EN-AU">1MHz</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">带宽的频点。系统设计的通信距离为</span>
				<span lang="EN-AU">10</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">米（</span>
				<span lang="EN-AU">0db</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">），如果增加发射功率（</span>
				<span lang="EN-AU">20db</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">），这一距离也可以达到</span>
				<span lang="EN-AU">100</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">米。</span>
		</p>
		<p class="MsoNormal" style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; MARGIN-LEFT: 36pt; TEXT-INDENT: 36pt; LINE-HEIGHT: 150%">
				<span lang="EN-AU">2. </span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">基带（</span>
				<span lang="EN-AU">Baseband,BB</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）和链路管理（</span>
				<span lang="EN-AU">LinkManager,LM</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）单元</span>
		</p>
		<p class="MsoNormal" style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; MARGIN-LEFT: 72pt; TEXT-INDENT: 36pt; LINE-HEIGHT: 150%" align="left">
				<span lang="EN-AU">BB</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">负责跳频和蓝芽数据及信息帧的传输。</span>
				<span lang="EN-AU">LM</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">负责连接的建立和拆除。它们实现的功能包括：对</span>
				<span lang="EN-AU">SCO</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">和</span>
				<span lang="EN-AU">ACL</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">连接方式的支持；差错控制，可以采用多种检纠错方式，其中包括前向纠错编码</span>
				<span lang="EN-AU">(FEC)</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">；物理层的认证与加密；链路管理。</span>
				<span lang="EN-AU">
						<br />
				</span>
				<span lang="EN-AU">4.</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">蓝牙软件协议栈的介绍</span>
				<span lang="EN-AU">
				</span>
		</p>
		<p class="MsoNormal" style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; MARGIN-LEFT: 36pt; TEXT-INDENT: 36pt; LINE-HEIGHT: 150%">
				<span lang="EN-AU">1</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）协议栈结构</span>
				<span lang="EN-AU">
				</span>
		</p>
		<p class="MsoNormal" style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; MARGIN-LEFT: 36pt; TEXT-INDENT: 36pt; LINE-HEIGHT: 150%">
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">底部协议层包括链路管理协议（</span>
				<span lang="EN-AU">Link Manager Protocol, LMP</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）和基带（</span>
				<span lang="EN-AU">Baseband</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）控制部分。链路管理协议实现链路的建立、认证及链路配置等。其中的服务项目包括：接收和发送数据、设备号请求、链路地址查询、建立连接、认证与加密、协商并建立连接方式、确定分组的帧类型、设置监听方式、设置保持方式以及设置休眠方式等。基带（</span>
				<span lang="EN-AU">Baseband</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）控制部分负责跳频和蓝芽数据及信息帧的传输，包括对纠错编码的支持，对</span>
				<span lang="EN-AU">SCO</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">和</span>
				<span lang="EN-AU">ACL</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">包的组织，流控等。</span>
				<span lang="EN-AU">
				</span>
		</p>
		<p class="MsoNormal" style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; MARGIN-LEFT: 36pt; TEXT-INDENT: 36pt; LINE-HEIGHT: 150%">
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">中间协议层包括逻辑链路控制和适应协议（</span>
				<span lang="EN-AU">Logical Link Control and Adaptation Protocol,L2CAP</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）、服务发现协议（</span>
				<span lang="EN-AU">Service Discovery Protocol,SDP</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）、串口仿真协议</span>
				<span lang="EN-AU">RFCOMM</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">和电话通信协议（</span>
				<span lang="EN-AU">Telephony Control Protocol,TCS</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">），对象交换协议（</span>
				<span lang="EN-AU">Object Exchange</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，</span>
				<span lang="EN-AU">OBEX)</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">。</span>
				<span lang="EN-AU">L2CAP</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">完成数据的拆装、服务质量和协议复用等功能，是其他上层协议实现的基础。</span>
				<span lang="EN-AU">SDP</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">为上层应用程序提供一种机制来发现网络中可用的服务及其特性。</span>
				<span lang="EN-AU">RFCOMM</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">基于</span>
				<span lang="EN-AU">ETSI</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">标准</span>
				<span lang="EN-AU">TS07.10</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">在</span>
				<span lang="EN-AU">L2CAP</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">上仿真</span>
				<span lang="EN-AU">9</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">针</span>
				<span lang="EN-AU">RS232</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">串口的功能。</span>
				<span lang="EN-AU">TCS</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">提供蓝芽设备间话音和数据的呼叫控制信令。对象交换协议（</span>
				<span lang="EN-AU">Object Exchange</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，</span>
				<span lang="EN-AU">OBEX)</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">是</span>
				<span lang="EN-AU">Extended Systems</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">公司为红外通信的高层应用开发的协议，现在已成功的应用于蓝牙协议栈中。</span>
				<span lang="EN-AU">
				</span>
		</p>
		<p class="MsoNormal" style="MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; MARGIN-LEFT: 36pt; TEXT-INDENT: 36pt; LINE-HEIGHT: 150%">
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">在</span>
				<span lang="EN-AU">BB</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">和</span>
				<span lang="EN-AU">LM</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">上与</span>
				<span lang="EN-AU">L2CAP</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">之间还有一个主机控制接口层（</span>
				<span lang="EN-AU">Host Controller Interface,HCI</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）。</span>
				<span lang="EN-AU">HCI</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">是蓝芽协议中软硬件之间的接口，它提供了一个调用下层</span>
				<span lang="EN-AU">BB</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">、</span>
				<span lang="EN-AU">LM</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">、状态和控制寄存器等硬件的统一命令接口。</span>
				<span lang="EN-AU">HCI</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">协议以上的协议软件实体运行在主机上，而</span>
				<span lang="EN-AU">HCI</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">以下的功能由蓝牙模块来完成，二者之间通过一个对两端透明的传输层进行交互。</span>
				<span lang="EN-AU">
						<p>
						</p>
				</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">吸收现成的协议有</span>
				<span lang="EN-AU">PPP</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">、</span>
				<span lang="EN-AU">TCP/IP</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">、</span>
				<span lang="EN-AU">OBEX </span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">、</span>
				<span lang="EN-AU">WAP… <br />摘自：<a href="http://www.wx800.com/msg/snapshot.php?doc_seq=6659"><font color="#5f9ea0">http://www.wx800.com/msg/snapshot.php?doc_seq=6659</font></a>#短距离无线互连领域里（IrDA和Bluetooth）的领导者</span>
		</p>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/18577.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-10-29 11:01 <a href="http://www.cnitblog.com/zouzheng/articles/18577.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> 移植u-boot到S3C2410</title><link>http://www.cnitblog.com/zouzheng/articles/18576.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Sun, 29 Oct 2006 02:57:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/18576.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/18576.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/18576.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/18576.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/18576.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 最近计划将u-boot移植到我的S3C2410板子上，我的S3C2410板子上没有Norflash，只有64M的Nandflash，因此Bootloader是从Nandflash启动的，感觉和SMDK基本上差不多，所以vivi源码不需要任何修改，编译后就可以下载到板子上并启动，然后通过xmoden协议下载linux和root到内存中并烧写到Nandflash中，非常方便，但是美中不足的是vivi不...&nbsp;&nbsp;<a href='http://www.cnitblog.com/zouzheng/articles/18576.html'>阅读全文</a><img src ="http://www.cnitblog.com/zouzheng/aggbug/18576.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-10-29 10:57 <a href="http://www.cnitblog.com/zouzheng/articles/18576.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>u-boot在s3c2410开发板上移植（NAND Flash Boot）过程</title><link>http://www.cnitblog.com/zouzheng/articles/17818.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Tue, 10 Oct 2006 02:11:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/17818.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/17818.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/17818.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/17818.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/17818.html</trackback:ping><description><![CDATA[
		<p>
				<font size="3">
						<strong>1）</strong>u-boot版本1.1.3，gcc version 3.3.3 (DENX ELDK 3.1.1 3.3.3-9)</font>
		</p>
		<p>
				<font size="3">
						<strong>2）</strong>在Makefile中加入<br />bks2410_config : unconfig<br /> @./mkconfig $(@:_config=) arm arm920t bks2410 NULL s3c24x0<br />我把我的板子起名叫bks2410，可以依自己的喜好修改</font>
		</p>
		<p>
				<font size="3">
						<strong>3）</strong>建立board/bks2410目录，拷贝board/smdk2410下的文件到board/bks2410目录，将smdk2410.c更名为bks2410.c</font>
		</p>
		<p>
				<font size="3">
						<strong>4）</strong>cp include/configs/smdk2410.h include/configs/bks2410.h</font>
		</p>
		<p>
				<font size="3">
						<strong>5）</strong>将arm-linux-gcc的目录加入到PATH环境变量中，我的是目录/opt/eldk/usr/bin:/opt/eldk/bin</font>
		</p>
		<p>
				<font size="3">
						<strong>6）</strong>测试编译能否成功：<br />make bks2410_config<br />make all ARCH=arm<br />生成u-boot.bin就OK了</font>
		</p>
		<p>
				<font size="3">
						<strong>7）</strong>依照你自己开发板的内存地址分配情况修改board/bks2410/memsetup.S文件，我的程序：<br />#include &lt;config.h&gt;<br />#include &lt;version.h&gt;</font>
		</p>
		<p>
				<font size="3">#define BWSCON 0x48000000</font>
		</p>
		<p>
				<font size="3">/* BWSCON */<br />#define DW8    (0x0)<br />#define DW16    (0x1)<br />#define DW32    (0x2)<br />#define WAIT    (0x1&lt;&lt;2)<br />#define UBLB    (0x1&lt;&lt;3)</font>
		</p>
		<p>
				<font size="3">#define B1_BWSCON    (DW32)<br />#define B2_BWSCON    (DW16)<br />#define B3_BWSCON    (DW16 + WAIT + UBLB)<br />#define B4_BWSCON    (DW16)<br />#define B5_BWSCON    (DW16)<br />#define B6_BWSCON    (DW32)<br />#define B7_BWSCON    (DW32)</font>
		</p>
		<p>
				<font size="3">/* BANK0CON */<br />#if 0<br />#define B0_Tacs    0x0 /*  0clk */<br />#define B0_Tcos    0x0 /*  0clk */<br />#define B0_Tacc    0x7 /* 14clk */<br />#define B0_Tcoh    0x0 /*  0clk */<br />#define B0_Tah    0x0 /*  0clk */<br />#define B0_Tacp    0x0<br />#define B0_PMC    0x0 /* normal */<br />#endif</font>
		</p>
		<p>
				<font size="3">#define B0_Tacs    0x3 /*  0clk */<br />#define B0_Tcos    0x3 /*  0clk */<br />#define B0_Tacc    0x7 /* 14clk */<br />#define B0_Tcoh    0x3 /*  0clk */<br />#define B0_Tah    0x3 /*  0clk */<br />#define B0_Tacp    0x1<br />#define B0_PMC    0x0 /* normal */</font>
		</p>
		<p>
				<font size="3">/* BANK1CON */<br />#if 0<br />#define B1_Tacs    0x0 /*  0clk */<br />#define B1_Tcos    0x0 /*  0clk */<br />#define B1_Tacc    0x7 /* 14clk */<br />#define B1_Tcoh    0x0 /*  0clk */<br />#define B1_Tah    0x0 /*  0clk */<br />#define B1_Tacp    0x0<br />#define B1_PMC    0x0<br />#endif</font>
		</p>
		<p>
				<font size="3">#define B1_Tacs    0x3 /*  0clk */<br />#define B1_Tcos    0x3 /*  0clk */<br />#define B1_Tacc    0x7 /* 14clk */<br />#define B1_Tcoh    0x3 /*  0clk */<br />#define B1_Tah    0x3 /*  0clk */<br />#define B1_Tacp    0x3<br />#define B1_PMC    0x0</font>
		</p>
		<p>
				<font size="3">#define B2_Tacs    0x0<br />#define B2_Tcos    0x0<br />#define B2_Tacc    0x7<br />#define B2_Tcoh    0x0<br />#define B2_Tah    0x0<br />#define B2_Tacp    0x0<br />#define B2_PMC    0x0</font>
		</p>
		<p>
				<font size="3">#if 0<br />#define B3_Tacs    0x0 /*  0clk */<br />#define B3_Tcos    0x3 /*  4clk */<br />#define B3_Tacc    0x7 /* 14clk */<br />#define B3_Tcoh    0x1 /*  1clk */<br />#define B3_Tah    0x0 /*  0clk */<br />#define B3_Tacp    0x3     /*  6clk */<br />#define B3_PMC    0x0 /* normal */<br />#endif</font>
		</p>
		<p>
				<font size="3">#define B3_Tacs    0x0 /*  0clk */<br />#define B3_Tcos    0x0 /*  4clk */<br />#define B3_Tacc    0x7 /* 14clk */<br />#define B3_Tcoh    0x0 /*  1clk */<br />#define B3_Tah    0x0 /*  0clk */<br />#define B3_Tacp    0x0     /*  6clk */<br />#define B3_PMC    0x0 /* normal */</font>
		</p>
		<p>
				<font size="3">#define B4_Tacs    0x0 /*  0clk */<br />#define B4_Tcos    0x0 /*  0clk */<br />#define B4_Tacc    0x7 /* 14clk */<br />#define B4_Tcoh    0x0 /*  0clk */<br />#define B4_Tah    0x0 /*  0clk */<br />#define B4_Tacp    0x0<br />#define B4_PMC    0x0 /* normal */</font>
		</p>
		<p>
				<font size="3">#define B5_Tacs    0x0 /*  0clk */<br />#define B5_Tcos    0x0 /*  0clk */<br />#define B5_Tacc    0x7 /* 14clk */<br />#define B5_Tcoh    0x0 /*  0clk */<br />#define B5_Tah    0x0 /*  0clk */<br />#define B5_Tacp    0x0<br />#define B5_PMC    0x0 /* normal */</font>
		</p>
		<p>
				<font size="3">#define B6_MT    0x3 /* SDRAM */<br />#define B6_Trcd     0x1<br />#define B6_SCAN    0x1 /* 9bit */</font>
		</p>
		<p>
				<font size="3">#define B7_MT    0x3 /* SDRAM */<br />#define B7_Trcd    0x1 /* 3clk */<br />#define B7_SCAN    0x1 /* 9bit */</font>
		</p>
		<p>
				<font size="3">/* REFRESH parameter */<br />#define REFEN    0x1 /* Refresh enable */<br />#define TREFMD    0x0 /* CBR(CAS before RAS)/Auto refresh */<br />#define Trp    0x0 /* 2clk */<br />#define Trc    0x3 /* 7clk */<br />#define Tchr    0x2 /* 3clk */<br />#define REFCNT    1113 /* period=15.6us, HCLK=60Mhz, (2048+1-15.6*60) */<br />/**************************************/</font>
		</p>
		<p>
				<font size="3">_TEXT_BASE:<br /> .word TEXT_BASE</font>
		</p>
		<p>
				<font size="3">.globl memsetup<br />memsetup:<br /> /* memory control configuration */<br /> /* make r0 relative the current location so that it */<br /> /* reads SMRDATA out of FLASH rather than memory ! */<br /> adr     r0, SMRDATA<br /> /*ldr r1, _TEXT_BASE*/<br /> /*sub r0, r0, r1*/<br /> ldr r1, =BWSCON /* Bus Width Status Controller */<br /> add     r2, r0, #13*4<br />0:<br /> ldr     r3, [r0], #4<br /> str     r3, [r1], #4<br /> cmp     r2, r0<br /> bne     0b</font>
		</p>
		<p>
				<font size="3"> /* everything is fine now */<br /> mov pc, lr</font>
		</p>
		<p>
				<font size="3"> .ltorg<br />/* the literal pools origin */</font>
		</p>
		<p>
				<font size="3">SMRDATA:<br />    .word (0+(B1_BWSCON&lt;&lt;4)+(B2_BWSCON&lt;&lt;8)+(B3_BWSCON&lt;&lt;12)+(B4_BWSCON&lt;&lt;16)+(B5_BWSCON&lt;&lt;20)+(B6_BWSCON&lt;&lt;24)+(B7_BWSCON&lt;&lt;28))<br />    .word ((B0_Tacs&lt;&lt;13)+(B0_Tcos&lt;&lt;11)+(B0_Tacc&lt;&lt;8)+(B0_Tcoh&lt;&lt;6)+(B0_Tah&lt;&lt;4)+(B0_Tacp&lt;&lt;2)+(B0_PMC))<br />    .word ((B1_Tacs&lt;&lt;13)+(B1_Tcos&lt;&lt;11)+(B1_Tacc&lt;&lt;8)+(B1_Tcoh&lt;&lt;6)+(B1_Tah&lt;&lt;4)+(B1_Tacp&lt;&lt;2)+(B1_PMC))<br />    .word ((B2_Tacs&lt;&lt;13)+(B2_Tcos&lt;&lt;11)+(B2_Tacc&lt;&lt;8)+(B2_Tcoh&lt;&lt;6)+(B2_Tah&lt;&lt;4)+(B2_Tacp&lt;&lt;2)+(B2_PMC))<br />    .word 0x1f7c/*((B3_Tacs&lt;&lt;13)+(B3_Tcos&lt;&lt;11)+(B3_Tacc&lt;&lt;8)+(B3_Tcoh&lt;&lt;6)+(B3_Tah&lt;&lt;4)+(B3_Tacp&lt;&lt;2)+(B3_PMC))*/<br />    .word ((B4_Tacs&lt;&lt;13)+(B4_Tcos&lt;&lt;11)+(B4_Tacc&lt;&lt;8)+(B4_Tcoh&lt;&lt;6)+(B4_Tah&lt;&lt;4)+(B4_Tacp&lt;&lt;2)+(B4_PMC))<br />    .word ((B5_Tacs&lt;&lt;13)+(B5_Tcos&lt;&lt;11)+(B5_Tacc&lt;&lt;8)+(B5_Tcoh&lt;&lt;6)+(B5_Tah&lt;&lt;4)+(B5_Tacp&lt;&lt;2)+(B5_PMC))<br />    .word ((B6_MT&lt;&lt;15)+(B6_Trcd&lt;&lt;2)+(B6_SCAN))<br />    .word ((B7_MT&lt;&lt;15)+(B7_Trcd&lt;&lt;2)+(B7_SCAN))<br />    .word ((REFEN&lt;&lt;23)+(TREFMD&lt;&lt;22)+(Trp&lt;&lt;20)+(Trc&lt;&lt;18)+(Tchr&lt;&lt;16)+REFCNT)<br />    .word 0x31<br />    .word 0x30<br />    .word 0x30<br />    <br /><strong>8）</strong>在board/bks2410加入NAND Flash读函数，建立nand_read.c，加入如下内容(copy from vivi)：<br />#include &lt;config.h&gt;</font>
		</p>
		<p>
				<font size="3">#define __REGb(x) (*(volatile unsigned char *)(x))<br />#define __REGi(x) (*(volatile unsigned int *)(x))<br />#define NF_BASE  0x4e000000<br />#define NFCONF  __REGi(NF_BASE + 0x0)<br />#define NFCMD  __REGb(NF_BASE + 0x4)<br />#define NFADDR  __REGb(NF_BASE + 0x8)<br />#define NFDATA  __REGb(NF_BASE + 0xc)<br />#define NFSTAT  __REGb(NF_BASE + 0x10)</font>
		</p>
		<p>
				<font size="3">#define BUSY 1<br />inline void wait_idle(void) {<br />    int i;</font>
		</p>
		<p>
				<font size="3">    while(!(NFSTAT &amp; BUSY))<br />      for(i=0; i&lt;10; i++);<br />}</font>
		</p>
		<p>
				<font size="3">#define NAND_SECTOR_SIZE 512<br />#define NAND_BLOCK_MASK  (NAND_SECTOR_SIZE - 1)</font>
		</p>
		<p>
				<font size="3">/* low level nand read function */<br />int<br />nand_read_ll(unsigned char *buf, unsigned long start_addr, int size)<br />{<br />    int i, j;</font>
		</p>
		<p>
				<font size="3">    if ((start_addr &amp; NAND_BLOCK_MASK) || (size &amp; NAND_BLOCK_MASK)) {<br />        return -1; /* invalid alignment */<br />    }</font>
		</p>
		<p>
				<font size="3">    /* chip Enable */<br />    NFCONF &amp;= ~0x800;<br />    for(i=0; i&lt;10; i++);</font>
		</p>
		<p>
				<font size="3">    for(i=start_addr; i &lt; (start_addr + size);) {<br />      /* READ0 */<br />      NFCMD = 0;</font>
		</p>
		<p>
				<font size="3">      /* Write Address */<br />      NFADDR = i &amp; 0xff;<br />      NFADDR = (i &gt;&gt; 9) &amp; 0xff;<br />      NFADDR = (i &gt;&gt; 17) &amp; 0xff;<br />      NFADDR = (i &gt;&gt; 25) &amp; 0xff;</font>
		</p>
		<p>
				<font size="3">      wait_idle();</font>
		</p>
		<p>
				<font size="3">      for(j=0; j &lt; NAND_SECTOR_SIZE; j++, i++) {<br /> *buf = (NFDATA &amp; 0xff);<br /> buf++;<br />      }<br />    }</font>
		</p>
		<p>
				<font size="3">    /* chip Disable */<br />    NFCONF |= 0x800; /* chip disable */</font>
		</p>
		<p>
				<font size="3">    return 0;<br />}</font>
		</p>
		<p>
				<font size="3">
						<strong>9）</strong>修改board/bks2410/Makefile为<br />OBJS := bks2410.o flash.o nand_read.o</font>
		</p>
		<p>
				<font size="3">
						<strong>10）</strong>修改cpu/arm920t/start.S文件<br />在ldr pc, _start_armboot之前加入：<br />#ifdef CONFIG_S3C2410_NAND_BOOT<br />  bl    copy_myself<br />  <br />  @ jump to ram<br />  ldr   r1, =on_the_ram<br />  add  pc, r1, #0<br />  nop<br />  nop<br />  1:    b     1b          @ infinite loop<br />  <br />on_the_ram:<br />#endif</font>
		</p>
		<p>
				<font size="3">在_start_armboot: .word start_armboot之后加入：<br />#ifdef CONFIG_S3C2410_NAND_BOOT<br />copy_myself:<br />  mov r10, lr<br />@ reset NAND<br />  mov r1, #NAND_CTL_BASE<br />  ldr   r2, =0xf830           @ initial value<br />  str   r2, [r1, #oNFCONF]<br />  ldr   r2, [r1, #oNFCONF]<br />  bic  r2, r2, #0x800              @ enable chip<br />  str   r2, [r1, #oNFCONF]<br />  mov r2, #0xff         @ RESET command<br />  strb r2, [r1, #oNFCMD]<br />  mov r3, #0                   @ wait</font>
		</p>
		<p>
				<font size="3">1:add  r3, r3, #0x1<br />  cmp r3, #0xa<br />  blt   1b<br />2:ldr   r2, [r1, #oNFSTAT]      @ wait ready<br />  tst    r2, #0x1<br />  beq  2b<br />  ldr   r2, [r1, #oNFCONF]<br />  orr  r2, r2, #0x800              @ disable chip<br />  str   r2, [r1, #oNFCONF]</font>
		</p>
		<p>
				<font size="3">@ get read to call C functions (for nand_read())<br />  ldr   sp, DW_STACK_START       @ setup stack pointer<br />  mov fp, #0                    @ no previous frame, so fp=0</font>
		</p>
		<p>
				<font size="3">@ copy vivi to RAM<br />  ldr   r0, =UBOOT_RAM_BASE<br />  mov     r1, #0x0<br />  mov r2, #0x20000<br />  bl    nand_read_ll<br />  tst    r0, #0x0<br />  beq  ok_nand_read</font>
		</p>
		<p>
				<font size="3">#ifdef CONFIG_DEBUG_LL<br />  bad_nand_read:<br />  ldr   r0, STR_FAIL<br />  ldr   r1, SerBase<br />  bl    PrintWord<br />1:b     1b          @ infinite loop<br />  #endif</font>
		</p>
		<p>
				<font size="3">ok_nand_read:<br />#ifdef CONFIG_DEBUG_LL<br />  ldr   r0, STR_OK<br />  ldr   r1, SerBase<br />  bl    PrintWord<br />#endif</font>
		</p>
		<p>
				<font size="3">@ verify<br />  mov r0, #0<br />  ldr   r1, =UBOOT_RAM_BASE<br />  mov r2, #0x400     @ 4 bytes * 1024 = 4K-bytes<br />go_next:<br />  ldr   r3, [r0], #4<br />  ldr   r4, [r1], #4<br />  teq   r3, r4<br />  bne  notmatch<br />  subs r2, r2, #4<br />  beq  done_nand_read<br />  bne  go_next</font>
		</p>
		<p>
				<font size="3">notmatch:<br />#ifdef CONFIG_DEBUG_LL<br />  sub  r0, r0, #4<br />  ldr   r1, SerBase<br />  bl    PrintHexWord<br />  ldr   r0, STR_FAIL<br />  ldr   r1, SerBase<br />  bl    PrintWord<br />#endif<br />1:b     1b<br />done_nand_read:<br />#ifdef CONFIG_DEBUG_LL<br />  ldr   r0, STR_OK<br />  ldr   r1, SerBase<br />  bl    PrintWord<br />#endif<br />  mov pc, r10<br />@ clear memory<br />@ r0: start address<br />@ r1: length<br />  mem_clear:<br />  mov r2, #0<br />  mov r3, r2<br />  mov r4, r2<br />  mov r5, r2<br />  mov r6, r2<br />  mov r7, r2<br />  mov r8, r2<br />  mov r9, r2</font>
		</p>
		<p>
				<font size="3">clear_loop:<br />  stmia      r0!, {r2-r9}<br />  subs r1, r1, #(8 * 4)<br />  bne  clear_loop<br />  mov pc, lr</font>
		</p>
		<p>
				<font size="3">#endif @ CONFIG_S3C2410_NAND_BOOT</font>
		</p>
		<p>
				<font size="3">在文件的最后加入：<br />   .align     2<br />DW_STACK_START:<br /> .word      STACK_BASE+STACK_SIZE-4</font>
		</p>
		<p>
				<font size="3">
						<strong>11）</strong>修改include/configs/bks2410.h文件，添加如下内容：<br />/*<br /> * Nandflash Boot<br /> */<br />#define CONFIG_S3C2410_NAND_BOOT 1<br />#define STACK_BASE    0x33f00000<br />#define STACK_SIZE    0x8000<br />#define UBOOT_RAM_BASE    0x33f80000<br />/* NAND Flash Controller */<br />#define NAND_CTL_BASE            0x4E000000<br />#define bINT_CTL(Nb)        __REG(INT_CTL_BASE + (Nb))<br />/* Offset */<br />#define oNFCONF               0x00<br />#define oNFCMD                0x04<br />#define oNFADDR               0x08<br />#define oNFDATA               0x0c<br />#define oNFSTAT               0x10<br />#define oNFECC                0x14</font>
		</p>
		<p>
				<font size="3">
						<strong>12）</strong>重新编译u-boot<br />make all ARCH=arm</font>
		</p>
		<p>
				<font size="3">
						<strong>13）</strong>通过jtag将u-boot烧写到flash中就可以从NAND flash启动了<br /></font>
		</p>
		<p>
				<font size="3">
						<strong>我的u-boot启动信息：</strong>
				</font>
		</p>
		<p>
				<font size="3">U-Boot 1.1.3 (May  6 2006 - 18:13:59)</font>
		</p>
		<p>
				<font size="3">U-Boot code: 33F80000 -&gt; 33F967C4  BSS: -&gt; 33F9AAAC<br />RAM Configuration:<br />Bank #0: 30000000 64 MB<br />Flash: 512 kB<br />*** Warning - bad CRC, using default environment</font>
		</p>
		<p>
				<font size="3">In:    serial<br />Out:   serial<br />Err:   serial<br />BKS2410 # </font>
		</p>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/17818.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-10-10 10:11 <a href="http://www.cnitblog.com/zouzheng/articles/17818.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>基于GPRS的SOCKET通信的应用研究</title><link>http://www.cnitblog.com/zouzheng/articles/17408.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Wed, 27 Sep 2006 12:33:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/17408.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/17408.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/17408.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/17408.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/17408.html</trackback:ping><description><![CDATA[
		<span style="font-size: 12px;">
				<font color="#666666">
						<b>摘要：</b>SOCKET通信是目前常用的通信方式之一。文中以8位单片机AT89C52作为微控制器，它利用自带的异步串口与电平转换芯片MAX232和GPRS模块连接，使用AT命令对GPRS模块LT8030进行控制，从而实现SOCKET通信。</font>
				<font color="#666666">详细介绍SOCKET通信中要用到的AT命令，并给出实现SOCKET通信的完整程序。<br /><br /><b>关键词：</b>SOCKET通信 GPRS 串行通信 AT命令<br /><br />引 言<br /><br />　
　GPRS（General Packet Radio
Service）是通用分组无线业务的简称，是一种以全球手机系统（GSM）为基础的数据传输技术[1]。GPRS网不但具有覆盖范围广、数据传输速度
快、通信质量高、永远在线和按流量计费等优点，而且其本身就是一个分组型数据网，支持TCP/IP协议，无需经过PSTN（公用电话交换网）等网络的转
接，可直接与Internet网互通。因此GPRS业务在无线上网、环境监测、交通监控、移动办公等行业中具有无可比拟的性价比优势。<br /><br />　
　在网络设置中有HTTP、SOCKET等类型。SOCKET是建立在传输层协议(主要是TCP和UDP)上的一种套接字规范，它定义两台计算机间进行通
信的规范（也是一种编程规范）。如果说两台计算机是利用一个通道进行通信，那么这个通道的两端就是两个套接字。套接字屏蔽了底层通信软件和具体操作系统的
差异，使得任何两台安装了TCP协议软件和实现了套接字规范的计算机之间的通信成为可能。<br /><br />1 系统组成<br /><br />　　系统的组成如图1所示。该系统利用现有的GPRS网络，单片机通过串口对GPRS模块进行收发控制，实现SOCKET通信。<br /><br />　　系统各部分的说明如下：<br /><br />　　① 单片机采用了AT89C52，它带有一个串口；<br /><br />　
　②
GPRS模块。本文以利事达信息技术有限公司开发的GPRS模块LT8030[2]为例。LT8030内嵌了完整的TCP/IP协议栈，包括TCP、
UDP、FTP、SOCKET、Telnet、POP3、SMTP、HTTP等，为用户提供了更简单的网络接口。LT8030采用的GPRS技术，无缝覆
盖、永远在线且按流量计费，紧密结合产品应用领域所遇到的实际问题，进行全面的优化和升级，使产品开发变得更容易、更快捷。它采用标准的RS232
接口，用户可以通过单片机或其他CPU的UART口，使用相应的AT命令对模块进行控制，达到使其产品可以轻松进入GPRS网络的目的。<br /><br />　
　③
服务器。建立SOCKET连接必须具有公网的IP地址，故应保证服务器中心计算机连接到Internet并且取得公网IP地址。在单片机对GPRS模块控
制之前，服务器端需运行SOCKET端口监听程序（此监听程序一般是现成的），并且设为监听状态，端口号也要设定，例如port：1024。<br /><br />2 单片机与GPRS模块的连接<br /><br />　
　单片机与GPRS模块一般采用串行异步通信接口，通信速度可设定，通常为9600
bps。采用RS232电缆方式进行连接时，数据传输的可靠性较好。单片机通过电平转换电路与GPRS模块连接，电路比较简单，电路原理图如图2所示。所
涉及的芯片MAX232用于串行通信接口与232通信接口之间的电平转换[3]。<br /><br /></font>
				<center>
						<font color="#666666">
								<img src="http://www.c114.net/technic/adm_news/images_upload/200512261420386596.jpg" />
						</font>
				</center>
				<font color="#666666">　<br /></font>
				<center>
						<font color="#666666">图1系统组成框图</font>
				</center>
				<font color="#666666">
						<br />
						<br />
				</font>
				<center>
						<font color="#666666">
								<img src="http://www.c114.net/technic/adm_news/images_upload/2005122614202952703.jpg" />
						</font>
				</center>
				<font color="#666666">　<br /></font>
				<center>
						<font color="#666666">图2单片机与GPRS模拟连接的电路原理图</font>
				</center>
				<font color="#666666">
						<br />
						<br />　
　MAX232的T1IN、T2IN、R1OUT、R2OUT为接TTL/CMOS电平的引脚；T1OUT、T2OUT、R1IN、R2IN为接
RS232电平的引脚。TTL/CMOS电平的T1IN、T2IN引脚应接AT89C52的串行发送引脚TXD；R1OUT、R2OUT应接
AT89C52的串行接收引脚RXD。与之对应，RS232电平的T1OUT、T2OUT应接GPRS模块的接收端RXD；R1IN、R2IN应接
GPRS模块的发送端TXD。<br /><br />　　现选用其中一路发送/接收,R1OUT接AT89C52的RXD，T1IN接AT89C52的TXD,T1OUT接GPRS模块的RXD, R1IN接GPRS模块的发送端TXD。因为MAX232具有驱动能力，所以不需要外加驱动电路。<br /><br />3 建立SOCKET连接的命令<br /><br />　　下面对SOCKET通信中要用到的一些AT命令[2]进行说明。<br /><br />　　3.1 基本设置<br /><br />　　① GPRS ISP 码。<br />　　AT IISP1=*99***1# //全国通用 <br /><br />　　② 登录用户名。<br />　　AT IUSRN=WAP//GPRS网络登录名 <br /><br />　　③ 登录密码。<br />　　AT IPWD=WAP// GPRS网络登录密码 <br /><br />　　④ MODEM 类型。<br />　　AT IMTYP=2 //定义GPRS MODEM <br /><br />　　⑤ 初始化命令。<br />　　AT IMIS=“AT CGDCONT=1,ip,CMNET”<br /><br />　　⑥ 域名服务器。<br />　　AT IDNS1=211.136.18.171<br />　　//DNS服务器地址，全国通用<br /><br />　　⑦ 扩展码（XRC）。<br />　　AT IXRC=0 <br /><br />　　3.2 SOCKET设置<br /><br />　　① 建立一个TCP通信。 <br />　　AT ISTCP：218.66.16.173,1024<cr><br />　
　建立SOCKET连接，218.66.16.173为应用服务中心计算机端IP地址（实际地址由实际情况决定），1024
为端口号（端口号由中心SOCKET端口监听程序设置决定）。
如果连接成功，LT8030返回I/xxx。xxx为LT8030中本次SOCKET连接的句柄号。中心监听程序会显示连接的终端IP地址。如果连接失
败，LT8030返回I/ERROR(xxx)。xxx为错误代码。 <br /><br />　　② 发送数据。<br />　　AT ISSND%：xxx,<string length="">：<string><br />　　发送数据，xxx为句柄，<string length="">为要发送的字符长度，<string>为要发送的数据。发送成功后，在中心端可看到终端发送的数据。最多一次能够发送5K以下的数据。 <br /><br />　　③ 查询SOCKET状态。<br />　　AT ISST：xxx<cr><br />　　查询SOCKET状态，xxx为句柄。 LT8030返回I/<socketstat>。如果<socketstat>= 000，表示该端口连接正常；如果<socketstat>≥1，LT8030通过该端口从中心接收存在Buffer 里的字节数；如果<socketstat>&lt;0，则SOCKET错误。<br /><br />　　④ 接收数据。<br />　　AT ISRCV：xxx<cr><br />　　xxx为句柄。该指令会读取LT8030通过该句柄从中心接收到的，存在Buffer 里的数据；Buffer最大可存储30K的数据。<br /><br />　　⑤ 关闭SOCKET通道。<br />　　AT ISCLS：xxx <br />　　关闭SOCKET通道，xxx为句柄。<br /><br />4 程序的设计　　<br /><br />　
　根据单片机与GPRS模块通信协议的约定，单片机串行口设为方式1，波特率为9 600
bps，8位UART，1位起始位，1位停止位，无奇偶校验。上电后，首先向GPRS模块发送基本设置命令，即ISP码、用户名及用户密码帧等，其中
ISP码必须为“*99***1#”，用户名和用户密码可以任意设置，但不能为空。在使用LT8030 GPRS
上网功能之前，必须正确设置这些参数。参数一旦设置后，即永久保存，以后无需重新再设(
以上设定为LT8030C出厂时的默认参数)。然后向GPRS模块发送SOCKET设置帧，如成功，则点和点通信环境已建立，接着就调用发送数据帧。<br /><br />　　开机上电后，程序在主函数中运行，单片机进行初始化。初始化包括设置串口工作方式、波特率，并初始化变量参数和标志位。<br /><br />　　编著注：实现SOCKET通信的完整程序，见本刊网站（<a href="http://www.dpj.com.cn/" target="_blank">www.dpj.com.cn</a>）。<br /><br />结语<br /><br />　
　随着计算机和通信技术的进步,当今社会的生产正朝着高效、准确和稳定的方向发展,这对数据传输的实时性、可靠性、信息量提出了更高的要求,为此介绍利用
GPRS模块实现SOCKET通信的系统。本文采用内嵌TCP/IP协议的GPRS模块LT8030，在8位微控制器AT89C52上实现了对
LT8030的控制，并实现了基于GPRS的SOCKET通信功能，具有外围器件少、电路简单、系统成本低等优点。<br /><br />　　　　　　　　　　　　　　　　<b>参考文献</b><br /><br />1 J R (Bud)Bates. 通用分组无线业务（GPRS）技术与应用. 朱洪波，等译. 北京:人民邮电出版社,2004<br />2 利事达信息技术有限公司.LT8030介绍<br />3 胡伟,季晓衡.单片机C程序设计及应用实例. 北京:人民邮电出版社,2003<br />4 张毅刚,彭喜元,等.MCS51单片机应用设计.哈尔滨:哈尔滨工业大学出版社,2002</cr></socketstat></socketstat></socketstat></socketstat></cr></string></string></string></string></cr></font>
		</span>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/17408.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-09-27 20:33 <a href="http://www.cnitblog.com/zouzheng/articles/17408.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>修改2.4的ud1341驱动程序(不过有点问题)</title><link>http://www.cnitblog.com/zouzheng/articles/15594.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Sat, 19 Aug 2006 13:32:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/15594.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/15594.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/15594.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/15594.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/15594.html</trackback:ping><description><![CDATA[
		<span class="postbody">声卡的驱动，可以放音了，录音好像还不行
<br /><br />
#include &lt;linux/module.h&gt;
<br />
#include &lt;linux/device.h&gt;
<br />
#include &lt;linux/init.h&gt;
<br />
#include &lt;linux/types.h&gt;
<br />
#include &lt;linux/fs.h&gt;
<br />
#include &lt;linux/mm.h&gt;
<br />
#include &lt;linux/slab.h&gt;
<br />
#include &lt;linux/delay.h&gt;
<br />
#include &lt;linux/sched.h&gt;
<br />
#include &lt;linux/poll.h&gt;
<br />
#include &lt;linux/interrupt.h&gt;
<br />
#include &lt;linux/errno.h&gt;
<br />
#include &lt;linux/sound.h&gt;
<br />
#include &lt;linux/soundcard.h&gt;
<br /><br />
#include &lt;linux/pm.h&gt;
<br /><br />
#include &lt;asm/uaccess.h&gt;
<br />
#include &lt;asm/io.h&gt;
<br />
#include &lt;asm/hardware.h&gt;
<br />
#include &lt;asm/semaphore.h&gt;
<br />
#include &lt;asm/dma.h&gt;
<br />
#include &lt;asm/arch/cpu_s3c2410.h&gt;
<br />
#include &lt;asm/arch/dma.h&gt;
<br />
#include &lt;asm/arch/regs-gpio.h&gt;
<br />
#include &lt;asm/arch/regs-iis.h&gt;
<br />
#include &lt;asm/hardware/clock.h&gt;
<br />
#include &lt;asm/arch/regs-clock.h&gt;
<br />
#include &lt;linux/dma-mapping.h&gt;
<br />
#include &lt;asm/dma-mapping.h&gt;
<br />
#include &lt;asm/arch/hardware.h&gt;
<br />
#include &lt;asm/arch/map.h&gt;
<br />
#include &lt;asm/arch/S3C2410.h&gt;
<br />
#define PFX "s3c2410-uda1341-superlp: "
<br /><br />
#define MAX_DMA_CHANNELS	0
<br /><br />
/* The S3C2410 has four internal DMA channels. */
<br /><br />
#define MAX_S3C2410_DMA_CHANNELS	S3C2410_DMA_CHANNELS
<br /><br />
#define DMA_CH0			0
<br />
#define DMA_CH1			1
<br />
#define DMA_CH2			2
<br />
#define DMA_CH3			3
<br /><br />
#define DMA_BUF_WR		1
<br />
#define DMA_BUF_RD		0
<br /><br /><br />
#define dma_wrreg(chan, reg, val) writel((val), (chan)-&gt;regs + (reg))
<br /><br /><br />
static struct clk	*iis_clock;
<br />
static void __iomem	*iis_base;
<br /><br />
static struct s3c2410_dma_client s3c2410iis_dma_out= {
<br />
	.name		= "I2SSDO",
<br />
};
<br /><br />
static struct s3c2410_dma_client s3c2410iis_dma_in = {
<br />
	.name		= "I2SSDI",
<br />
};
<br /><br /><br /><br />
#ifdef DEBUG
<br />
#define DPRINTK printk
<br />
#else
<br />
#define DPRINTK( x... )
<br />
#endif
<br /><br />
static void init_s3c2410_iis_bus_rx(void);
<br />
static void init_s3c2410_iis_bus_tx(void);
<br /><br />
#define DEF_VOLUME      65
<br /><br />
/* UDA1341 Register bits */
<br />
#define UDA1341_ADDR		0x14
<br /><br />
#define UDA1341_REG_DATA0	(UDA1341_ADDR + 0)
<br />
#define UDA1341_REG_STATUS	(UDA1341_ADDR + 2)
<br /><br />
/* status control */
<br />
#define STAT0			(0x00)
<br />
#define STAT0_RST               (1 &lt;&lt; 6)
<br />
#define STAT0_SC_MASK           (3 &lt;&lt; 4)
<br />
#define STAT0_SC_512FS          (0 &lt;&lt; 4)
<br />
#define STAT0_SC_384FS          (1 &lt;&lt; 4)
<br />
#define STAT0_SC_256FS          (2 &lt;&lt; 4)
<br />
#define STAT0_IF_MASK           (7 &lt;&lt; 1)
<br />
#define STAT0_IF_I2S            (0 &lt;&lt; 1)
<br />
#define STAT0_IF_LSB16          (1 &lt;&lt; 1)
<br />
#define STAT0_IF_LSB18          (2 &lt;&lt; 1)
<br />
#define STAT0_IF_LSB20          (3 &lt;&lt; 1)
<br />
#define STAT0_IF_MSB            (4 &lt;&lt; 1)
<br />
#define STAT0_IF_LSB16MSB       (5 &lt;&lt; 1)
<br />
#define STAT0_IF_LSB18MSB       (6 &lt;&lt; 1)
<br />
#define STAT0_IF_LSB20MSB       (7 &lt;&lt; 1)
<br />
#define STAT0_DC_FILTER         (1 &lt;&lt; 0)
<br />
#define STAT0_DC_NO_FILTER	(0 &lt;&lt; 0)
<br /><br />
#define STAT1			(0x80)
<br />
#define STAT1_DAC_GAIN          (1 &lt;&lt; 6)        /* gain of DAC */
<br />
#define STAT1_ADC_GAIN          (1 &lt;&lt; 5)        /* gain of ADC */
<br />
#define STAT1_ADC_POL           (1 &lt;&lt; 4)        /* polarity of ADC */
<br />
#define STAT1_DAC_POL           (1 &lt;&lt; 3)        /* polarity of DAC */
<br />
#define STAT1_DBL_SPD           (1 &lt;&lt; 2)        /* double speed playback */
<br />
#define STAT1_ADC_ON            (1 &lt;&lt; 1)        /* ADC powered */
<br />
#define STAT1_DAC_ON            (1 &lt;&lt; 0)        /* DAC powered */
<br /><br />
/* data0 direct control */
<br />
#define DATA0     		(0x00)
<br />
#define DATA0_VOLUME_MASK       (0x3f)
<br />
#define DATA0_VOLUME(x)         (x)
<br /><br />
#define DATA1     		(0x40)
<br />
#define DATA1_BASS(x)           ((x) &lt;&lt; 2)
<br />
#define DATA1_BASS_MASK         (15 &lt;&lt; 2)
<br />
#define DATA1_TREBLE(x)         ((x))
<br />
#define DATA1_TREBLE_MASK       (3)
<br /><br />
#define DATA2     		(0x80)
<br />
#define DATA2_PEAKAFTER         (0x1 &lt;&lt; 5)
<br />
#define DATA2_DEEMP_NONE        (0x0 &lt;&lt; 3)
<br />
#define DATA2_DEEMP_32KHz       (0x1 &lt;&lt; 3)
<br />
#define DATA2_DEEMP_44KHz       (0x2 &lt;&lt; 3)
<br />
#define DATA2_DEEMP_48KHz       (0x3 &lt;&lt; 3)
<br />
#define DATA2_MUTE              (0x1 &lt;&lt; 2)
<br />
#define DATA2_FILTER_FLAT       (0x0 &lt;&lt; 0)
<br />
#define DATA2_FILTER_MIN        (0x1 &lt;&lt; 0)
<br />
#define DATA2_FILTER_MAX        (0x3 &lt;&lt; 0)
<br />
/* data0 extend control */
<br />
#define EXTADDR(n)              (0xc0 | (n))
<br />
#define EXTDATA(d)              (0xe0 | (d))
<br /><br />
#define EXT0                    0
<br />
#define EXT0_CH1_GAIN(x)        (x)
<br />
#define EXT1                    1
<br />
#define EXT1_CH2_GAIN(x)        (x)
<br />
#define EXT2                    2
<br />
#define EXT2_MIC_GAIN_MASK      (7 &lt;&lt; 2)
<br />
#define EXT2_MIC_GAIN(x)        ((x) &lt;&lt; 2)
<br />
#define EXT2_MIXMODE_DOUBLEDIFF (0)
<br />
#define EXT2_MIXMODE_CH1        (1)
<br />
#define EXT2_MIXMODE_CH2        (2)
<br />
#define EXT2_MIXMODE_MIX        (3)
<br />
#define EXT4                    4
<br />
#define EXT4_AGC_ENABLE         (1 &lt;&lt; 4)
<br />
#define EXT4_INPUT_GAIN_MASK    (3)
<br />
#define EXT4_INPUT_GAIN(x)      ((x) &amp; 3)
<br />
#define EXT5                    5
<br />
#define EXT5_INPUT_GAIN(x)      ((x) &gt;&gt; 2)
<br />
#define EXT6                    6
<br />
#define EXT6_AGC_CONSTANT_MASK  (7 &lt;&lt; 2)
<br />
#define EXT6_AGC_CONSTANT(x)    ((x) &lt;&lt; 2)
<br />
#define EXT6_AGC_LEVEL_MASK     (3)
<br />
#define EXT6_AGC_LEVEL(x)       (x)
<br /><br />
#define AUDIO_NAME		"UDA1341"
<br />
#define AUDIO_NAME_VERBOSE	"UDA1341 audio driver"
<br /><br />
#define AUDIO_FMT_MASK          (AFMT_S16_LE)
<br />
#define AUDIO_FMT_DEFAULT       (AFMT_S16_LE)
<br /><br />
#define AUDIO_CHANNELS_DEFAULT	2
<br />
#define AUDIO_RATE_DEFAULT	44100
<br /><br />
#define AUDIO_NBFRAGS_DEFAULT	8
<br />
#define AUDIO_FRAGSIZE_DEFAULT	8192 
<br /><br />
#define S_CLOCK_FREQ	384
<br />
#define PCM_ABS(a) (a &lt; 0 ? -a : a)
<br /><br />
typedef struct {
<br />
	int size;		/* buffer size */
<br />
	char *start;		/* point to actual buffer */
<br />
	dma_addr_t dma_addr;	/* physical buffer address */
<br />
	struct semaphore sem;	/* down before touching the buffer */
<br />
	int master;		/* owner for buffer allocation, contain size when true */
<br />
} audio_buf_t;
<br /><br />
typedef struct {
<br />
	audio_buf_t *buffers;	/* pointer to audio buffer structures */
<br />
	audio_buf_t *buf;	/* current buffer used by read/write */
<br />
	u_int buf_idx;		/* index for the pointer above */
<br />
	u_int fragsize;		/* fragment i.e. buffer size */
<br />
	u_int nbfrags;		/* nbr of fragments */
<br />
	dmach_t dma_ch;		/* DMA channel (channel2 for audio) */
<br />
	u_int dma_ok;
<br />
} audio_stream_t;
<br /><br />
static audio_stream_t output_stream;
<br />
static audio_stream_t input_stream; /* input */
<br /><br />
#define NEXT_BUF(_s_,_b_) { \
<br />
        (_s_)-&gt;_b_##_idx++; \
<br />
        (_s_)-&gt;_b_##_idx %= (_s_)-&gt;nbfrags; \
<br />
        (_s_)-&gt;_b_ = (_s_)-&gt;buffers + (_s_)-&gt;_b_##_idx; }
<br /><br /><br />
static u_int audio_rate;
<br />
static int audio_channels;
<br />
static int audio_fmt;
<br />
static u_int audio_fragsize;
<br />
static u_int audio_nbfrags;
<br /><br /><br />
static int audio_rd_refcount;
<br />
static int audio_wr_refcount;
<br />
#define audio_active		(audio_rd_refcount | audio_wr_refcount)
<br /><br />
static int audio_dev_dsp;
<br />
static int audio_dev_mixer;
<br />
static int audio_mix_modcnt;
<br /><br />
static int uda1341_volume;
<br />
static u8 uda_sampling;
<br />
static int uda1341_boost;
<br />
static int mixer_igain=0x4; /* -6db*/
<br /><br />
static void uda1341_l3_address(u8 data)
<br />
{
<br />
	int i;
<br />
	unsigned long flags;
<br /><br />
	local_irq_save(flags);
<br /><br />
//	write_gpio_bit(GPIO_L3MODE, 0);
<br />
	s3c2410_gpio_setpin(S3C2410_GPB2,0);		
<br />
//	write_gpio_bit(GPIO_L3CLOCK, 1);
<br />
	s3c2410_gpio_setpin(S3C2410_GPB4,1);	
<br />
	udelay(1);
<br /><br />
	for (i = 0; i &lt; 8; i++) {
<br />
		if (data &amp; 0x1) {
<br />
			s3c2410_gpio_setpin(S3C2410_GPB4,0);			
<br />
			s3c2410_gpio_setpin(S3C2410_GPB3,1);			
<br />
			udelay(1);
<br />
			s3c2410_gpio_setpin(S3C2410_GPB4,1);
<br />
		} else {
<br />
			s3c2410_gpio_setpin(S3C2410_GPB4,0);			
<br />
			s3c2410_gpio_setpin(S3C2410_GPB3,0);			
<br />
			udelay(1);
<br />
			s3c2410_gpio_setpin(S3C2410_GPB4,1);			
<br />
		}
<br />
		data &gt;&gt;= 1;
<br />
	}
<br /><br />
	s3c2410_gpio_setpin(S3C2410_GPB2,1);	
<br />
	s3c2410_gpio_setpin(S3C2410_GPB4,1);	
<br />
	local_irq_restore(flags);
<br />
}
<br /><br />
static void uda1341_l3_data(u8 data)
<br />
{
<br />
	int i;
<br />
	unsigned long flags;
<br /><br />
	local_irq_save(flags);
<br />
	udelay(1);
<br /><br />
	for (i = 0; i &lt; 8; i++) {
<br />
		if (data &amp; 0x1) {
<br />
			s3c2410_gpio_setpin(S3C2410_GPB4,0);			
<br />
			s3c2410_gpio_setpin(S3C2410_GPB3,1);				
<br />
			udelay(1);
<br />
			s3c2410_gpio_setpin(S3C2410_GPB4,1);				
<br />
		} else {
<br />
			s3c2410_gpio_setpin(S3C2410_GPB4,0);				
<br />
			s3c2410_gpio_setpin(S3C2410_GPB3,0);				
<br />
			udelay(1);
<br />
			s3c2410_gpio_setpin(S3C2410_GPB4,1);				
<br />
		}
<br /><br />
		data &gt;&gt;= 1;
<br />
	}
<br /><br />
	local_irq_restore(flags);
<br />
}
<br /><br />
static void audio_clear_buf(audio_stream_t * s)
<br />
{
<br />
    	DPRINTK("audio_clear_buf\n");
<br /><br />
	if(s-&gt;dma_ok) s3c2410_dma_ctrl(s-&gt;dma_ch, S3C2410_DMAOP_FLUSH);
<br /><br />
	if (s-&gt;buffers) {
<br />
		int frag;
<br /><br />
		for (frag = 0; frag &lt; s-&gt;nbfrags; frag++) {
<br />
			if (!s-&gt;buffers[frag].master)
<br />
				continue;
<br />
			dma_free_coherent(NULL,
<br />
					s-&gt;buffers[frag].master,
<br />
					s-&gt;buffers[frag].start,
<br />
					s-&gt;buffers[frag].dma_addr);
<br />
		}
<br />
		kfree(s-&gt;buffers);
<br />
		s-&gt;buffers = NULL;
<br />
	}
<br /><br />
	s-&gt;buf_idx = 0;
<br />
	s-&gt;buf = NULL;
<br />
}
<br /><br />
static int audio_setup_buf(audio_stream_t * s)
<br />
{
<br />
	int frag;
<br />
	int dmasize = 0;
<br />
	char *dmabuf = 0;
<br />
	dma_addr_t dmaphys = 0;
<br /><br />
	if (s-&gt;buffers)
<br />
		return -EBUSY;
<br /><br />
	s-&gt;nbfrags = audio_nbfrags;
<br />
	s-&gt;fragsize = audio_fragsize;
<br /><br />
	s-&gt;buffers = (audio_buf_t *)
<br />
	    kmalloc(sizeof(audio_buf_t) * s-&gt;nbfrags, GFP_KERNEL);
<br />
	if (!s-&gt;buffers)
<br />
		goto err;
<br />
	memset(s-&gt;buffers, 0, sizeof(audio_buf_t) * s-&gt;nbfrags);
<br /><br />
	for (frag = 0; frag &lt; s-&gt;nbfrags; frag++) {
<br />
		audio_buf_t *b = &amp;s-&gt;buffers[frag];
<br /><br />
		if (!dmasize) {
<br />
			dmasize = (s-&gt;nbfrags - frag) * s-&gt;fragsize;
<br />
			do {
<br />
				dmabuf = dma_alloc_coherent(NULL, dmasize, &amp;dmaphys, GFP_KERNEL|GFP_DMA);
<br />
				if (!dmabuf) 
<br />
				    	dmasize -= s-&gt;fragsize;
<br />
			} while (!dmabuf &amp;&amp; dmasize);
<br />
			if (!dmabuf)
<br />
				goto err;
<br />
			b-&gt;master = dmasize;
<br />
		}
<br /><br />
		b-&gt;start = dmabuf;
<br />
		b-&gt;dma_addr = dmaphys;
<br />
		sema_init(&amp;b-&gt;sem, 1);
<br />
		DPRINTK("buf %d: start %p dma %d\n", frag, b-&gt;start, b-&gt;dma_addr);
<br /><br />
		dmabuf += s-&gt;fragsize;
<br />
		dmaphys += s-&gt;fragsize;
<br />
		dmasize -= s-&gt;fragsize;
<br />
	}
<br /><br />
	s-&gt;buf_idx = 0;
<br />
	s-&gt;buf = &amp;s-&gt;buffers[0];
<br />
	return 0;
<br /><br />
      err:
<br />
	printk(AUDIO_NAME ": unable to allocate audio memory\n ");
<br />
	audio_clear_buf(s);
<br />
	return -ENOMEM;
<br />
}
<br /><br />
static void audio_dmaout_done_callback(s3c2410_dma_chan_t *ch, void *buf, int size,
<br />
				   s3c2410_dma_buffresult_t result)
<br />
{
<br />
	audio_buf_t *b = (audio_buf_t *) buf;
<br />
	up(&amp;b-&gt;sem);
<br />
	wake_up(&amp;b-&gt;sem.wait);
<br />
}
<br /><br />
static void audio_dmain_done_callback(s3c2410_dma_chan_t *ch, void *buf, int size,
<br />
				   s3c2410_dma_buffresult_t result)
<br />
{
<br />
	audio_buf_t *b = (audio_buf_t *) buf;
<br />
  	b-&gt;size = size;
<br />
	up(&amp;b-&gt;sem);
<br />
	wake_up(&amp;b-&gt;sem.wait);		
<br />
}
<br />
 /* using when write */
<br />
static int audio_sync(struct file *file)
<br />
{
<br />
	audio_stream_t *s = &amp;output_stream;
<br />
	audio_buf_t *b = s-&gt;buf;
<br /><br />
	DPRINTK("audio_sync\n");
<br /><br />
	if (!s-&gt;buffers)
<br />
		return 0;
<br /><br />
	if (b-&gt;size != 0) {
<br />
		down(&amp;b-&gt;sem);
<br />
		s3c2410_dma_enqueue(s-&gt;dma_ch, (void *) b, b-&gt;dma_addr, b-&gt;size);
<br />
		b-&gt;size = 0;
<br />
		NEXT_BUF(s, buf);
<br />
	}
<br /><br />
	b = s-&gt;buffers + ((s-&gt;nbfrags + s-&gt;buf_idx - 1) % s-&gt;nbfrags);
<br />
	if (down_interruptible(&amp;b-&gt;sem))
<br />
		return -EINTR;
<br />
	up(&amp;b-&gt;sem);
<br /><br />
	return 0;
<br />
}
<br /><br />
static inline int copy_from_user_mono_stereo(char *to, const char *from, int count)
<br />
{
<br />
	u_int *dst = (u_int *)to;
<br />
	const char *end = from + count;
<br /><br />
	if (verify_area(VERIFY_READ, from, count))
<br />
		return -EFAULT;
<br /><br />
	if ((int)from &amp; 0x2) {
<br />
		u_int v;
<br />
		__get_user(v, (const u_short *)from); from += 2;
<br />
		*dst++ = v | (v &lt;&lt; 16);
<br />
	}
<br /><br />
	while (from &lt; end-2) {
<br />
		u_int v, x, y;
<br />
		__get_user(v, (const u_int *)from); from += 4;
<br />
		x = v &lt;&lt; 16;
<br />
		x |= x &gt;&gt; 16;
<br />
		y = v &gt;&gt; 16;
<br />
		y |= y &lt;&lt; 16;
<br />
		*dst++ = x;
<br />
		*dst++ = y;
<br />
	}
<br /><br />
	if (from &lt; end) {
<br />
		u_int v;
<br />
		__get_user(v, (const u_short *)from);
<br />
		*dst = v | (v &lt;&lt; 16);
<br />
	}
<br /><br />
	return 0;
<br />
}
<br /><br /><br />
static ssize_t smdk2410_audio_write(struct file *file, const char *buffer, 
<br />
				    size_t count, loff_t * ppos)
<br />
{
<br />
	const char *buffer0 = buffer;
<br />
	audio_stream_t *s = &amp;output_stream;
<br />
	int chunksize, ret = 0;
<br /><br />
	DPRINTK("audio_write : start count=%d\n", count);
<br /><br />
	switch (file-&gt;f_flags &amp; O_ACCMODE) {
<br />
	  	case O_WRONLY:
<br />
	  	case O_RDWR:
<br />
			break;
<br />
	  	default:
<br />
		  	return -EPERM;
<br />
	}
<br /><br />
	if (!s-&gt;buffers &amp;&amp; audio_setup_buf(s))
<br />
		return -ENOMEM;
<br /><br />
	count &amp;= ~0x03;
<br /><br />
	while (count &gt; 0) {
<br />
		audio_buf_t *b = s-&gt;buf;
<br /><br />
		if (file-&gt;f_flags &amp; O_NONBLOCK) {
<br />
			ret = -EAGAIN;
<br />
			if (down_trylock(&amp;b-&gt;sem))
<br />
				break;
<br />
		} else {
<br />
			ret = -ERESTARTSYS;
<br />
			if (down_interruptible(&amp;b-&gt;sem))
<br />
				break;
<br />
		}
<br /><br />
		if (audio_channels == 2) {
<br />
			chunksize = s-&gt;fragsize - b-&gt;size;
<br />
			if (chunksize &gt; count)
<br />
				chunksize = count;
<br />
			DPRINTK("write %d to %d\n", chunksize, s-&gt;buf_idx);
<br />
			if (copy_from_user(b-&gt;start + b-&gt;size, buffer, chunksize)) {
<br />
				up(&amp;b-&gt;sem);
<br />
				return -EFAULT;
<br />
			}
<br />
			b-&gt;size += chunksize;
<br />
		} else {
<br />
			chunksize = (s-&gt;fragsize - b-&gt;size) &gt;&gt; 1;
<br /><br />
			if (chunksize &gt; count)
<br />
				chunksize = count;
<br />
			DPRINTK("write %d to %d\n", chunksize*2, s-&gt;buf_idx);
<br />
			if (copy_from_user_mono_stereo(b-&gt;start + b-&gt;size, 
<br />
				    		       buffer, chunksize)) {
<br />
				up(&amp;b-&gt;sem);
<br />
				return -EFAULT;
<br />
			}
<br /><br />
			b-&gt;size += chunksize*2;
<br />
		}
<br /><br />
		buffer += chunksize;
<br />
		count -= chunksize;
<br />
		if (b-&gt;size &lt; s-&gt;fragsize) {
<br />
			up(&amp;b-&gt;sem);
<br />
			break;
<br />
		}
<br /><br />
		if((ret = s3c2410_dma_enqueue(s-&gt;dma_ch, (void *) b, b-&gt;dma_addr, b-&gt;size))) {
<br />
			printk(PFX"dma enqueue failed.\n");
<br />
			return ret;
<br />
		}
<br />
		b-&gt;size = 0;
<br />
		NEXT_BUF(s, buf);
<br />
	}
<br /><br />
	if ((buffer - buffer0))
<br />
		ret = buffer - buffer0;
<br /><br />
	DPRINTK("audio_write : end count=%d\n\n", ret);
<br /><br />
	return ret;
<br />
}
<br /><br /><br />
static ssize_t smdk2410_audio_read(struct file *file, char *buffer, 
<br />
                                        size_t count, loff_t * ppos)
<br />
{
<br />
	const char *buffer0 = buffer;
<br />
        audio_stream_t *s = &amp;input_stream; 
<br />
        int chunksize, ret = 0;
<br /><br />
        DPRINTK("audio_read: count=%d\n", count);
<br /><br />
        if (ppos != &amp;file-&gt;f_pos)
<br />
                return -ESPIPE;
<br /><br />
	if (!s-&gt;buffers) {
<br />
		int i;
<br /><br />
                 if (audio_setup_buf(s))
<br />
                         return -ENOMEM;
<br /><br />
                 for (i = 0; i &lt; s-&gt;nbfrags; i++) {
<br />
                         audio_buf_t *b = s-&gt;buf; 
<br />
                         down(&amp;b-&gt;sem); 
<br />
                         s3c2410_dma_enqueue(s-&gt;dma_ch, (void *) b, b-&gt;dma_addr, s-&gt;fragsize);
<br />
                         NEXT_BUF(s, buf);
<br />
                 }
<br />
         }
<br /><br />
        while (count &gt; 0) {
<br />
                audio_buf_t *b = s-&gt;buf;
<br /><br />
                /* Wait for a buffer to become full */
<br />
                if (file-&gt;f_flags &amp; O_NONBLOCK) {
<br />
                        ret = -EAGAIN;
<br />
                        if (down_trylock(&amp;b-&gt;sem))
<br />
                                break;
<br />
                } else {
<br />
                        ret = -ERESTARTSYS;
<br />
                        if (down_interruptible(&amp;b-&gt;sem))
<br />
                                break;
<br />
                }
<br /><br />
                chunksize = b-&gt;size;
<br />
		if (chunksize &gt; count)
<br />
                        chunksize = count;
<br />
                DPRINTK("read %d from %d\n", chunksize, s-&gt;buf_idx);
<br />
                if (copy_to_user(buffer, b-&gt;start + s-&gt;fragsize - b-&gt;size,
<br />
				       	chunksize)) {
<br />
                        up(&amp;b-&gt;sem);
<br />
                        return -EFAULT;
<br />
                }
<br /><br />
                b-&gt;size -= chunksize;
<br /><br />
                buffer += chunksize;
<br />
                count -= chunksize;
<br />
                if (b-&gt;size &gt; 0) {
<br />
                        up(&amp;b-&gt;sem);
<br />
                        break;
<br />
                }
<br /><br />
                /* Make current buffer available for DMA again */
<br />
                s3c2410_dma_enqueue(s-&gt;dma_ch, (void *) b, b-&gt;dma_addr, s-&gt;fragsize);
<br /><br />
                NEXT_BUF(s, buf);
<br />
        }
<br /><br />
        if ((buffer - buffer0))
<br />
                ret = buffer - buffer0;
<br /><br />
     //   DPRINTK("audio_read: return=%d\n", ret);
<br /><br />
        return ret;
<br />
}
<br /><br /><br />
static unsigned int smdk2410_audio_poll(struct file *file, 
<br />
					struct poll_table_struct *wait)
<br />
{
<br />
	unsigned int mask = 0;
<br />
	int i;
<br /><br />
	DPRINTK("audio_poll(): mode=%s\n",
<br />
		(file-&gt;f_mode &amp; FMODE_WRITE) ? "w" : "");
<br /><br />
	if (file-&gt;f_mode &amp; FMODE_READ) {
<br />
                if (!input_stream.buffers &amp;&amp; audio_setup_buf(&amp;input_stream))
<br />
                        return -ENOMEM;
<br />
                poll_wait(file, &amp;input_stream.buf-&gt;sem.wait, wait);
<br /><br />
                for (i = 0; i &lt; input_stream.nbfrags; i++) {
<br />
                        if (atomic_read(&amp;input_stream.buffers[i].sem.count) &gt; 0)
<br />
                                mask |= POLLIN | POLLWRNORM;
<br />
				break;
<br />
                }
<br />
        }
<br /><br /><br />
	if (file-&gt;f_mode &amp; FMODE_WRITE) {
<br />
		if (!output_stream.buffers &amp;&amp; audio_setup_buf(&amp;output_stream))
<br />
			return -ENOMEM;
<br />
		poll_wait(file, &amp;output_stream.buf-&gt;sem.wait, wait);
<br /><br />
		for (i = 0; i &lt; output_stream.nbfrags; i++) {
<br />
			if (atomic_read(&amp;output_stream.buffers[i].sem.count) &gt; 0)
<br />
				mask |= POLLOUT | POLLWRNORM;
<br />
				break;
<br />
		}
<br />
	}
<br /><br />
	DPRINTK("audio_poll() returned mask of %s\n",
<br />
		(mask &amp; POLLOUT) ? "w" : "");
<br /><br />
	return mask;
<br />
}
<br /><br /><br />
static loff_t smdk2410_audio_llseek(struct file *file, loff_t offset, 
<br />
				    int origin)
<br />
{
<br />
            return -ESPIPE;
<br />
}
<br /><br /><br />
static int smdk2410_mixer_ioctl(struct inode *inode, struct file *file, 
<br />
                                unsigned int cmd, unsigned long arg)
<br />
{
<br />
        int ret;
<br />
        long val = 0;
<br /><br />
	switch (cmd) {
<br />
		case SOUND_MIXER_INFO:
<br />
		{
<br />
			mixer_info info;
<br />
			strncpy(info.id, "UDA1341", sizeof(info.id));
<br />
			strncpy(info.name,"Philips UDA1341", sizeof(info.name));
<br />
			info.modify_counter = audio_mix_modcnt;
<br />
			return copy_to_user((void *)arg, &amp;info, sizeof(info));
<br />
		}
<br /><br />
		case SOUND_OLD_MIXER_INFO:
<br />
		{
<br />
			_old_mixer_info info;
<br />
			strncpy(info.id, "UDA1341", sizeof(info.id));
<br />
			strncpy(info.name,"Philips UDA1341", sizeof(info.name));
<br />
			return copy_to_user((void *)arg, &amp;info, sizeof(info));
<br />
		}
<br /><br />
		case SOUND_MIXER_READ_STEREODEVS:
<br />
			return put_user(0, (long *) arg);
<br /><br />
		case SOUND_MIXER_READ_CAPS:
<br />
			val = SOUND_CAP_EXCL_INPUT;
<br />
			return put_user(val, (long *) arg);
<br /><br />
		case SOUND_MIXER_WRITE_VOLUME:
<br />
			ret = get_user(val, (long *) arg);
<br />
			if (ret)
<br />
				return ret;
<br />
			uda1341_volume = 63 - (((val &amp; 0xff) + 1) * 63) / 100;
<br />
			uda1341_l3_address(UDA1341_REG_DATA0);
<br />
			uda1341_l3_data(uda1341_volume);
<br />
			break;
<br /><br />
		case SOUND_MIXER_READ_VOLUME:
<br />
			val = ((63 - uda1341_volume) * 100) / 63;
<br />
			val |= val &lt;&lt; 8;
<br />
			return put_user(val, (long *) arg);
<br /><br />
		case SOUND_MIXER_READ_IGAIN:
<br />
			val = ((31- mixer_igain) * 100) / 31;
<br />
			return put_user(val, (int *) arg);
<br /><br />
		case SOUND_MIXER_WRITE_IGAIN:
<br />
			ret = get_user(val, (int *) arg);
<br />
			if (ret)
<br />
				return ret;
<br />
			mixer_igain = 31 - (val * 31 / 100);		
<br />
			/* use mixer gain channel 1*/
<br />
			uda1341_l3_address(UDA1341_REG_DATA0);
<br />
			uda1341_l3_data(EXTADDR(EXT0));
<br />
			uda1341_l3_data(EXTDATA(EXT0_CH1_GAIN(mixer_igain)));			
<br />
			break;
<br /><br />
		default:
<br />
			DPRINTK("mixer ioctl %u unknown\n", cmd);
<br />
			return -ENOSYS;
<br />
	}			
<br /><br />
	audio_mix_modcnt++;
<br />
	return 0;
<br />
}
<br /><br /><br />
static int iispsr_value(int s_bit_clock, int sample_rate)
<br />
{
<br />
        int i, prescaler = 0;
<br />
        unsigned long tmpval;
<br />
        unsigned long tmpval384;
<br />
        unsigned long tmpval384min = 0xffff;
<br /><br />
 	tmpval384 = clk_get_rate(iis_clock) / s_bit_clock;
<br /><br />
        for (i = 0; i &lt; 32; i++) {
<br />
                tmpval = tmpval384/(i+1);
<br />
                if (PCM_ABS((sample_rate - tmpval)) &lt; tmpval384min) {
<br />
                        tmpval384min = PCM_ABS((sample_rate - tmpval));
<br />
                        prescaler = i;
<br />
                }
<br />
        }
<br /><br />
        DPRINTK("prescaler = %d\n", prescaler);
<br /><br />
        return prescaler;
<br />
}
<br /><br /><br />
static long audio_set_dsp_speed(long val)
<br />
{
<br />
	unsigned int prescaler;
<br />
	prescaler=(IISPSR_A(iispsr_value(S_CLOCK_FREQ, val))
<br />
                | IISPSR_B(iispsr_value(S_CLOCK_FREQ, val)));
<br />
	writel(prescaler, iis_base + S3C2410_IISPSR);
<br /><br />
	printk(PFX "audio_set_dsp_speed:%ld prescaler:%i\n",val,prescaler);
<br />
	return (audio_rate = val);
<br />
}
<br /><br />
static int smdk2410_audio_ioctl(struct inode *inode, struct file *file, 
<br />
                                uint cmd, ulong arg)
<br />
{
<br />
	long val;
<br /><br />
	switch (cmd) {
<br />
	  	case SNDCTL_DSP_SETFMT:
<br />
			get_user(val, (long *) arg);
<br />
		  	if (val &amp; AUDIO_FMT_MASK) {
<br />
			    	audio_fmt = val;
<br />
			    	break;
<br />
		  	} else
<br />
				return -EINVAL;
<br /><br />
	  	case SNDCTL_DSP_CHANNELS:
<br />
	  	case SNDCTL_DSP_STEREO:
<br />
		  	get_user(val, (long *) arg);
<br />
		  	if (cmd == SNDCTL_DSP_STEREO)
<br />
			  	val = val ? 2 : 1;
<br />
		  	if (val != 1 &amp;&amp; val != 2)
<br />
			  	return -EINVAL;
<br />
		  	audio_channels = val;
<br />
		  	break;
<br /><br />
	  	case SOUND_PCM_READ_CHANNELS:
<br />
		  	put_user(audio_channels, (long *) arg);
<br />
		 	break;
<br /><br />
	  	case SNDCTL_DSP_SPEED:
<br />
		  	get_user(val, (long *) arg);
<br />
		  	val = audio_set_dsp_speed(val);
<br />
                        if (val &lt; 0) 
<br />
				return -EINVAL;
<br />
		  	put_user(val, (long *) arg);
<br />
		  	break;
<br /><br />
	  	case SOUND_PCM_READ_RATE:
<br />
		  	put_user(audio_rate, (long *) arg);
<br />
		  	break;
<br /><br />
	  	case SNDCTL_DSP_GETFMTS:
<br />
		  	put_user(AUDIO_FMT_MASK, (long *) arg);
<br />
		  	break;
<br /><br />
	  	case SNDCTL_DSP_GETBLKSIZE:
<br />
			if(file-&gt;f_mode &amp; FMODE_WRITE)
<br />
		  		return put_user(audio_fragsize, (long *) arg);
<br />
			else		
<br />
				return put_user(audio_fragsize, (int *) arg);
<br /><br />
	  	case SNDCTL_DSP_SETFRAGMENT:
<br />
		        if (file-&gt;f_mode &amp; FMODE_WRITE) {	
<br />
		  		if (output_stream.buffers)
<br />
			  		return -EBUSY;
<br />
		  		get_user(val, (long *) arg);
<br />
		  		audio_fragsize = 1 &lt;&lt; (val &amp; 0xFFFF);
<br />
		  		if (audio_fragsize &lt; 16)
<br />
			  		audio_fragsize = 16;
<br />
		  		if (audio_fragsize &gt; 16384)
<br />
			  		audio_fragsize = 16384;
<br />
		  		audio_nbfrags = (val &gt;&gt; 16) &amp; 0x7FFF;
<br />
				if (audio_nbfrags &lt; 2)
<br />
					audio_nbfrags = 2;
<br />
		  		if (audio_nbfrags * audio_fragsize &gt; 128 * 1024)
<br />
			  		audio_nbfrags = 128 * 1024 / audio_fragsize;
<br />
		  		if (audio_setup_buf(&amp;output_stream))
<br />
			  		return -ENOMEM;
<br /><br />
			}
<br />
			if (file-&gt;f_mode &amp; FMODE_READ) {
<br />
				if (input_stream.buffers)
<br />
					return -EBUSY;
<br />
				get_user(val, (int *) arg);
<br />
				audio_fragsize =  1 &lt;&lt; (val &amp; 0xFFFF);
<br />
				if (audio_fragsize &lt; 16)
<br />
					audio_fragsize = 16;
<br />
				if (audio_fragsize &gt; 16384)
<br />
                                        audio_fragsize = 16384;
<br />
                                audio_nbfrags = (val &gt;&gt; 16) &amp; 0x7FFF;
<br />
                                if (audio_nbfrags &lt; 2)
<br />
                                        audio_nbfrags = 2;
<br />
                                if (audio_nbfrags * audio_fragsize &gt; 128 * 1024)
<br />
                                        audio_nbfrags = 128 * 1024 / audio_fragsize;
<br />
                                if (audio_setup_buf(&amp;input_stream))
<br />
                                        return -ENOMEM;
<br /><br />
			}
<br />
		 	break;
<br /><br />
	  	case SNDCTL_DSP_SYNC:
<br />
		  	return audio_sync(file);
<br /><br />
	  	case SNDCTL_DSP_GETOSPACE:
<br />
		{
<br />
			audio_stream_t *s = &amp;output_stream;
<br />
			audio_buf_info *inf = (audio_buf_info *) arg;
<br />
			int err = verify_area(VERIFY_WRITE, inf, sizeof(*inf));
<br />
			int i;
<br />
			int frags = 0, bytes = 0;
<br /><br />
			if (err)
<br />
				return err;
<br />
			for (i = 0; i &lt; s-&gt;nbfrags; i++) {
<br />
				if (atomic_read(&amp;s-&gt;buffers[i].sem.count) &gt; 0) {
<br />
					if (s-&gt;buffers[i].size == 0) frags++;
<br />
					bytes += s-&gt;fragsize - s-&gt;buffers[i].size;
<br />
				}
<br />
			}
<br />
			put_user(frags, &amp;inf-&gt;fragments);
<br />
			put_user(s-&gt;nbfrags, &amp;inf-&gt;fragstotal);
<br />
			put_user(s-&gt;fragsize, &amp;inf-&gt;fragsize);
<br />
			put_user(bytes, &amp;inf-&gt;bytes);
<br />
			break;
<br />
		}
<br /><br />
		case SNDCTL_DSP_GETISPACE:
<br />
		{
<br />
			audio_stream_t *s = &amp;input_stream;
<br />
			audio_buf_info *inf = (audio_buf_info *) arg;
<br />
			int err = verify_area(VERIFY_WRITE, inf, sizeof(*inf));
<br />
			int i;
<br />
			int frags = 0, bytes = 0;
<br /><br />
			if (!(file-&gt;f_mode &amp; FMODE_READ))
<br />
                                return -EINVAL;
<br /><br />
			if (err)
<br />
				return err;
<br />
			for(i = 0; i &lt; s-&gt;nbfrags; i++){
<br />
			if (atomic_read(&amp;s-&gt;buffers[i].sem.count) &gt; 0)
<br />
                                {
<br />
                                        if (s-&gt;buffers[i].size == s-&gt;fragsize)
<br />
                                                frags++;
<br />
                                        bytes += s-&gt;buffers[i].size;
<br />
                                }
<br />
                        }
<br />
			put_user(frags, &amp;inf-&gt;fragments);
<br />
                        put_user(s-&gt;nbfrags, &amp;inf-&gt;fragstotal);
<br />
                        put_user(s-&gt;fragsize, &amp;inf-&gt;fragsize);
<br />
                        put_user(bytes, &amp;inf-&gt;bytes);
<br />
                        break;
<br />
		}
<br />
	  	case SNDCTL_DSP_RESET:
<br />
			if (file-&gt;f_mode &amp; FMODE_READ) {
<br />
                                audio_clear_buf(&amp;input_stream);
<br />
                        }
<br />
                        if (file-&gt;f_mode &amp; FMODE_WRITE) {
<br />
                                audio_clear_buf(&amp;output_stream);
<br />
                        }
<br />
                        return 0;
<br />
		case SNDCTL_DSP_NONBLOCK:
<br />
			file-&gt;f_flags |= O_NONBLOCK;
<br />
                        return 0;
<br />
	 	case SNDCTL_DSP_POST:
<br />
	      	case SNDCTL_DSP_SUBDIVIDE:
<br />
	      	case SNDCTL_DSP_GETCAPS:
<br />
	      	case SNDCTL_DSP_GETTRIGGER:
<br />
	      	case SNDCTL_DSP_SETTRIGGER:
<br />
	      	case SNDCTL_DSP_GETIPTR:
<br />
	      	case SNDCTL_DSP_GETOPTR:
<br />
	      	case SNDCTL_DSP_MAPINBUF:
<br />
	      	case SNDCTL_DSP_MAPOUTBUF:
<br />
	      	case SNDCTL_DSP_SETSYNCRO:
<br />
	      	case SNDCTL_DSP_SETDUPLEX:
<br />
		  	return -ENOSYS;
<br />
	  	default:
<br />
		  	return smdk2410_mixer_ioctl(inode, file, cmd, arg);
<br />
	}
<br /><br />
	return 0;
<br />
}
<br /><br /><br />
static int smdk2410_audio_open(struct inode *inode, struct file *file)
<br />
{
<br />
	int cold = !audio_active;
<br /><br />
	DPRINTK("audio_open\n");
<br />
	if ((file-&gt;f_flags &amp; O_ACCMODE) == O_RDONLY) {
<br />
		if (audio_rd_refcount || audio_wr_refcount)
<br />
			return -EBUSY;
<br />
		audio_rd_refcount++;
<br />
	} else if ((file-&gt;f_flags &amp; O_ACCMODE) == O_WRONLY) {
<br />
		if (audio_wr_refcount)
<br />
			return -EBUSY;
<br />
		audio_wr_refcount++;
<br />
	} else if ((file-&gt;f_flags &amp; O_ACCMODE) == O_RDWR) {
<br />
		if (audio_rd_refcount || audio_wr_refcount)
<br />
			return -EBUSY;
<br />
		audio_rd_refcount++;
<br />
		audio_wr_refcount++;
<br />
	} else
<br />
		return -EINVAL;
<br /><br />
	if (cold) {
<br />
		audio_rate = AUDIO_RATE_DEFAULT;
<br />
		audio_channels = AUDIO_CHANNELS_DEFAULT;
<br />
		audio_fragsize = AUDIO_FRAGSIZE_DEFAULT;
<br />
		audio_nbfrags = AUDIO_NBFRAGS_DEFAULT;
<br />
		if ((file-&gt;f_mode &amp; FMODE_WRITE)){
<br />
				init_s3c2410_iis_bus_tx();
<br />
				audio_clear_buf(&amp;output_stream);
<br />
		}
<br />
		if ((file-&gt;f_mode &amp; FMODE_READ)){
<br />
				init_s3c2410_iis_bus_rx();
<br />
				audio_clear_buf(&amp;input_stream);
<br />
		}
<br />
	}
<br />
	return 0;
<br />
}
<br /><br /><br />
static int smdk2410_mixer_open(struct inode *inode, struct file *file)
<br />
{
<br />
	return 0;
<br />
}
<br /><br /><br />
static int smdk2410_audio_release(struct inode *inode, struct file *file)
<br />
{
<br />
	DPRINTK("audio_release\n");
<br /><br />
	if (file-&gt;f_mode &amp; FMODE_READ) {
<br />
		  	if (audio_rd_refcount == 1)
<br />
				audio_clear_buf(&amp;input_stream);
<br />
			  	audio_rd_refcount = 0;
<br />
	}
<br /><br />
	if(file-&gt;f_mode &amp; FMODE_WRITE) {
<br />
		  	if (audio_wr_refcount == 1) {
<br />
			    	audio_sync(file);
<br />
			    	audio_clear_buf(&amp;output_stream);
<br />
			    	audio_wr_refcount = 0;
<br />
		    	}
<br />
	  	}
<br /><br />
	return 0;
<br />
}
<br /><br /><br />
static int smdk2410_mixer_release(struct inode *inode, struct file *file)
<br />
{
<br />
	return 0;
<br />
}
<br /><br /><br />
static struct file_operations smdk2410_audio_fops = {
<br />
	llseek:		smdk2410_audio_llseek,
<br />
	write:		smdk2410_audio_write,
<br />
	read:		smdk2410_audio_read,
<br />
	poll:		smdk2410_audio_poll,
<br />
	ioctl:		smdk2410_audio_ioctl,
<br />
	open:		smdk2410_audio_open,
<br />
	release:	smdk2410_audio_release
<br />
};
<br /><br />
static struct file_operations smdk2410_mixer_fops = {
<br />
	ioctl:		smdk2410_mixer_ioctl,
<br />
	open:		smdk2410_mixer_open,
<br />
	release:	smdk2410_mixer_release
<br />
};
<br /><br />
static void init_uda1341(void)
<br />
{
<br />
	unsigned long flags;
<br /><br />
  	uda1341_volume = 62 - ((DEF_VOLUME * 61) / 100);
<br />
	uda1341_boost = 0;
<br />
  	uda_sampling = DATA2_DEEMP_NONE;
<br />
	uda_sampling &amp;= ~(DATA2_MUTE);
<br /><br /><br />
	local_irq_save(flags);
<br /><br />
	s3c2410_gpio_setpin(S3C2410_GPB2,1);
<br /><br />
	s3c2410_gpio_setpin(S3C2410_GPB4,1);
<br />
	local_irq_restore(flags);
<br /><br />
	uda1341_l3_address(UDA1341_REG_STATUS);
<br />
	uda1341_l3_data(0x40 | STAT0_SC_256FS | STAT0_IF_MSB|STAT0_DC_FILTER); // reset uda1341
<br />
        	uda1341_l3_data(STAT1 | STAT1_ADC_ON | STAT1_DAC_ON);
<br /><br />
        	uda1341_l3_address(UDA1341_REG_DATA0);
<br />
	uda1341_l3_data(DATA0 |DATA0_VOLUME(0x0));  // maximum volume
<br />
	uda1341_l3_data(DATA1 |DATA1_BASS(uda1341_boost)| DATA1_TREBLE(0));
<br />
        	uda1341_l3_data(uda_sampling); /* --;;*/ 
<br />
	uda1341_l3_data(EXTADDR(EXT2));
<br />
	uda1341_l3_data(EXTDATA(EXT2_MIC_GAIN(0x6)) | EXT2_MIXMODE_CH1);
<br /><br />
}
<br /><br />
static void init_s3c2410_iis_bus(void){
<br />
	writel(0, iis_base + S3C2410_IISPSR);
<br />
	writel(0, iis_base + S3C2410_IISCON);
<br />
	writel(0, iis_base + S3C2410_IISMOD);
<br />
	writel(0, iis_base + S3C2410_IISFCON);
<br />
	clk_disable(iis_clock);
<br />
}
<br />
static void init_s3c2410_iis_bus_rx(void){
<br />
	unsigned int iiscon, iismod, iisfcon;
<br />
	char *dstr;
<br />
	//Kill everything...
<br />
	writel(0, iis_base + S3C2410_IISPSR);
<br />
	writel(0, iis_base + S3C2410_IISCON);
<br />
	writel(0, iis_base + S3C2410_IISMOD);
<br />
	writel(0, iis_base + S3C2410_IISFCON);
<br /><br />
	clk_enable(iis_clock);
<br /><br />
	iiscon = iismod = iisfcon = 0;
<br /><br />
	//Setup basic stuff
<br />
	iiscon |= S3C2410_IISCON_PSCEN;				// Enable prescaler
<br />
	iiscon |= S3C2410_IISCON_IISEN;				// Enable interface
<br /><br />
	iismod |= S3C2410_IISMOD_MASTER;			// Set interface to Master Mode
<br />
	iismod |= S3C2410_IISMOD_LR_LLOW;			// Low for left channel
<br />
	iismod |= S3C2410_IISMOD_MSB;				// IIS format
<br />
	iismod |= S3C2410_IISMOD_16BIT;				// Serial data bit/channel is 16 bit
<br />
	iismod |= S3C2410_IISMOD_384FS;				// Master clock freq = 384 fs
<br />
	iismod |= S3C2410_IISMOD_32FS;				// 32 fs
<br /><br />
	iisfcon|= S3C2410_IISFCON_RXDMA;			//Set RX FIFO acces mode to DMA
<br />
	iisfcon|= S3C2410_IISFCON_TXDMA;			//Set RX FIFO acces mode to DMA
<br /><br />
	iiscon |= S3C2410_IISCON_RXDMAEN;	//Enable RX DMA service request
<br />
	iiscon |= S3C2410_IISCON_TXIDLE;	//Set TX channel idle
<br />
	iismod |= S3C2410_IISMOD_RXMODE;	//Set RX Mode
<br />
	iisfcon|= S3C2410_IISFCON_RXENABLE;	//Enable RX Fifo
<br />
	dstr="RX";
<br />
	//setup the prescaler
<br />
	audio_set_dsp_speed(audio_rate);
<br /><br />
	//iiscon has to be set last - it enables the interface
<br />
	writel(iismod, iis_base + S3C2410_IISMOD);
<br />
	writel(iisfcon, iis_base + S3C2410_IISFCON);
<br />
	writel(iiscon, iis_base + S3C2410_IISCON);
<br />
}
<br /><br />
static void init_s3c2410_iis_bus_tx(void)
<br />
{
<br />
	unsigned int iiscon, iismod, iisfcon;
<br />
	char *dstr;
<br /><br />
	//Kill everything...
<br />
	writel(0, iis_base + S3C2410_IISPSR);
<br />
	writel(0, iis_base + S3C2410_IISCON);
<br />
	writel(0, iis_base + S3C2410_IISMOD);
<br />
	writel(0, iis_base + S3C2410_IISFCON);
<br /><br />
	clk_enable(iis_clock);
<br /><br /><br />
	iiscon = iismod = iisfcon = 0;
<br /><br />
	//Setup basic stuff
<br />
	iiscon |= S3C2410_IISCON_PSCEN;				// Enable prescaler
<br />
	iiscon |= S3C2410_IISCON_IISEN;				// Enable interface
<br /><br />
	iismod |= S3C2410_IISMOD_MASTER;			// Set interface to Master Mode
<br />
	iismod |= S3C2410_IISMOD_LR_LLOW;			// Low for left channel
<br />
	iismod |= S3C2410_IISMOD_MSB;				// MSB format
<br />
	iismod |= S3C2410_IISMOD_16BIT;				// Serial data bit/channel is 16 bit
<br />
	iismod |= S3C2410_IISMOD_256FS;				// Master clock freq = 384 fs
<br />
	iismod |= S3C2410_IISMOD_32FS;				// 32 fs
<br /><br />
	iisfcon|= S3C2410_IISFCON_RXDMA;			//Set RX FIFO acces mode to DMA
<br />
	iisfcon|= S3C2410_IISFCON_TXDMA;			//Set TX FIFO acces mode to DMA
<br /><br />
	iiscon |= S3C2410_IISCON_TXDMAEN;	//Enable TX DMA service request
<br />
	iiscon |= S3C2410_IISCON_RXIDLE;	//Set RX channel idle
<br />
	iismod |= S3C2410_IISMOD_TXMODE;	//Set TX Mode
<br />
	iisfcon|= S3C2410_IISFCON_TXENABLE;	//Enable TX Fifo
<br />
	dstr="TX";
<br /><br />
	//setup the prescaler
<br />
	audio_set_dsp_speed(audio_rate);
<br /><br /><br />
	//iiscon has to be set last - it enables the interface
<br />
	writel(iismod, iis_base + S3C2410_IISMOD);
<br />
	writel(iisfcon, iis_base + S3C2410_IISFCON);
<br />
	writel(iiscon, iis_base + S3C2410_IISCON);
<br />
}
<br /><br />
static int __init audio_init_dma(audio_stream_t * s, char *desc)
<br />
{
<br />
	int ret ;
<br />
	s3c2410_dmasrc_t 	source;
<br />
	int 			hwcfg;
<br />
	unsigned long 		devaddr;
<br />
	dmach_t			channel;
<br />
	int 			dcon;
<br />
	unsigned int		flags = 0;
<br /><br />
	if(s-&gt;dma_ch == DMA_CH2){
<br />
		channel = 2;
<br />
		source  = S3C2410_DMASRC_MEM;
<br />
		hwcfg	= 3;
<br />
		devaddr	= 0x55000010;
<br />
		dcon	= 1&lt;&lt;31; 
<br />
		flags	= S3C2410_DMAF_AUTOSTART;
<br /><br />
		ret = s3c2410_dma_request(s-&gt;dma_ch, &amp;s3c2410iis_dma_out, NULL);
<br />
		s3c2410_dma_devconfig(channel, source, hwcfg, devaddr);
<br />
		s3c2410_dma_config(channel, 2, dcon);
<br />
		s3c2410_dma_set_buffdone_fn(channel, audio_dmaout_done_callback);
<br />
		s3c2410_dma_setflags(channel, flags);
<br />
		s-&gt;dma_ok = 1;
<br />
		return ret;
<br />
	}
<br />
	else if(s-&gt;dma_ch == DMA_CH1){
<br />
		ret = s3c2410_dma_request(s-&gt;dma_ch, &amp;s3c2410iis_dma_in, NULL);
<br />
	    		s3c2410_dma_set_buffdone_fn(s-&gt;dma_ch, audio_dmain_done_callback);
<br />
		return ret ;			
<br />
	}
<br />
	else
<br />
		return 1;
<br />
}
<br /><br />
static int audio_clear_dma(audio_stream_t * s,s3c2410_dma_client_t *client)
<br />
{
<br />
	s3c2410_dma_set_buffdone_fn(s-&gt;dma_ch, NULL);
<br />
	s3c2410_dma_free(s-&gt;dma_ch, client);
<br />
	return 0;
<br />
}
<br /><br />
static int s3c2410iis_probe(struct device *dev) {
<br />
	struct platform_device *pdev = to_platform_device(dev);
<br />
	struct resource *res;
<br /><br />
	unsigned long flags;
<br />
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
<br />
	if (res == NULL) {
<br />
		printk(KERN_INFO PFX "failed to get memory region resouce\n");
<br />
		return -ENOENT;
<br />
	}
<br /><br />
	iis_base = (void *)S3C2410_VA_IIS ;
<br />
	if (iis_base == 0) {
<br />
		printk(KERN_INFO PFX "failed to ioremap() region\n");
<br />
		return -EINVAL;
<br />
	}
<br /><br />
	iis_clock = clk_get(dev, "iis");
<br />
	if (iis_clock == NULL) {
<br />
		printk(KERN_INFO PFX "failed to find clock source\n");
<br />
		return -ENOENT;
<br />
	}
<br /><br />
	clk_use(iis_clock);
<br /><br />
	local_irq_save(flags);
<br /><br />
	/* GPB 4: L3CLOCK, OUTPUT */
<br />
 	s3c2410_gpio_cfgpin(S3C2410_GPB4, S3C2410_GPB4_OUTP);
<br />
	/* GPB 3: L3DATA, OUTPUT */
<br />
	s3c2410_gpio_cfgpin(S3C2410_GPB3,S3C2410_GPB3_OUTP);
<br />
	/* GPB 2: L3MODE, OUTPUT */
<br />
	s3c2410_gpio_cfgpin(S3C2410_GPB2,S3C2410_GPB2_OUTP);
<br />
	/* GPE 3: I2SSDI */
<br />
	s3c2410_gpio_cfgpin(S3C2410_GPE3,S3C2410_GPE3_I2SSDI);	
<br />
	/* GPE 0: I2SLRCK */
<br />
	s3c2410_gpio_cfgpin(S3C2410_GPE0,S3C2410_GPE0_I2SLRCK);
<br />
	/* GPE 1: I2SSCLK */
<br />
	s3c2410_gpio_cfgpin(S3C2410_GPE1,S3C2410_GPE1_I2SSCLK);	
<br />
	/* GPE 2: CDCLK */
<br />
	s3c2410_gpio_cfgpin(S3C2410_GPE2,S3C2410_GPE2_CDCLK);		
<br />
	/* GPE 4: I2SSDO */
<br />
	s3c2410_gpio_cfgpin(S3C2410_GPE4,S3C2410_GPE4_I2SSDO);
<br /><br />
	local_irq_restore(flags);
<br /><br />
	init_s3c2410_iis_bus();
<br /><br />
	init_uda1341();
<br /><br />
	output_stream.dma_ch = DMA_CH2;
<br /><br />
	if (audio_init_dma(&amp;output_stream, "UDA1341 out")) {
<br />
		audio_clear_dma(&amp;output_stream,&amp;s3c2410iis_dma_out);
<br />
		printk( KERN_WARNING AUDIO_NAME_VERBOSE 
<br />
			": unable to get DMA channels\n" );
<br />
		return -EBUSY;
<br />
	}
<br /><br />
	input_stream.dma_ch = DMA_CH1;
<br /><br />
   	if (audio_init_dma(&amp;input_stream, "UDA1341 in")) {
<br />
                        audio_clear_dma(&amp;input_stream,&amp;s3c2410iis_dma_in);
<br />
     	           printk( KERN_WARNING AUDIO_NAME_VERBOSE
<br />
                        ": unable to get DMA channels\n" );
<br />
                        return -EBUSY;
<br />
        	}
<br /><br />
	audio_dev_dsp = register_sound_dsp(&amp;smdk2410_audio_fops, -1);
<br />
	audio_dev_mixer = register_sound_mixer(&amp;smdk2410_mixer_fops, -1);
<br /><br />
	printk(AUDIO_NAME_VERBOSE " initialized\n");
<br /><br />
	return 0;
<br />
}
<br /><br /><br />
static int s3c2410iis_remove(struct device *dev) {
<br />
	unregister_sound_dsp(audio_dev_dsp);
<br />
	unregister_sound_mixer(audio_dev_mixer);
<br />
	audio_clear_dma(&amp;output_stream,&amp;s3c2410iis_dma_out);
<br />
	audio_clear_dma(&amp;input_stream,&amp;s3c2410iis_dma_in); /* input */
<br />
	printk(AUDIO_NAME_VERBOSE " unloaded\n");
<br />
	return 0;
<br />
}
<br /><br /><br />
static struct device_driver s3c2410iis_driver = {
<br />
        .name           = "s3c2410-iis",
<br />
        .bus            = &amp;platform_bus_type,
<br />
        .probe          = s3c2410iis_probe,
<br />
        .remove         = s3c2410iis_remove,
<br />
};
<br /><br />
static int __init s3c2410_uda1341_init(void) {
<br />
	memzero(&amp;input_stream, sizeof(audio_stream_t));
<br />
	memzero(&amp;output_stream, sizeof(audio_stream_t));
<br />
	return driver_register(&amp;s3c2410iis_driver);
<br />
}
<br /><br />
static void __exit s3c2410_uda1341_exit(void) {
<br />
	driver_unregister(&amp;s3c2410iis_driver);
<br />
}
<br /><br /><br />
module_init(s3c2410_uda1341_init);
<br />
module_exit(s3c2410_uda1341_exit);
<br /><br />
MODULE_LICENSE("GPL");
<br />
MODULE_AUTHOR("superlp&lt;xinshengtaier@eyou.com&gt;");
<br />
MODULE_DESCRIPTION("S3C2410 uda1341 sound driver");</span>
		<span class="postbody">
				<br />
				<script type="text/javascript">
						<!--
				google_ad_client = "pub-3152530285624674";
				google_ad_width = 468;
				google_ad_height = 15;
				google_ad_format = "468x15_0ads_al";
				google_ad_channel ="";
				//-->
				</script>
				<script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
				</script>
				<iframe name="google_ads_frame" src="http://pagead2.googlesyndication.com/pagead/ads?client=ca-pub-3152530285624674&amp;dt=1155991035527&amp;prev_fmts=468x60_as&amp;format=468x15_0ads_al&amp;output=html&amp;url=http%3A%2F%2Fwww.linuxfans.org%2Fnuke%2Fmodules.php%3Fname%3DForums%26file%3Dviewtopic%26t%3D112750%26postdays%3D0%26postorder%3Dasc%26start%3D30&amp;ref=http%3A%2F%2Fwww.hhcn.com%2Fcgi-bin%2Ftopic.cgi%3Fforum%3D3%26topic%3D426%26show%3D0&amp;cc=2&amp;u_h=768&amp;u_w=1024&amp;u_ah=714&amp;u_aw=1024&amp;u_cd=24&amp;u_tz=300&amp;u_his=1&amp;u_nplug=2&amp;u_nmime=3" marginwidth="0" marginheight="0" vspace="0" hspace="0" allowtransparency="true" frameborder="0" height="15" scrolling="no" width="468">
				</iframe>
		</span>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/15594.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-08-19 21:32 <a href="http://www.cnitblog.com/zouzheng/articles/15594.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux系统下USB摄像头驱动开发</title><link>http://www.cnitblog.com/zouzheng/articles/15529.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Fri, 18 Aug 2006 13:06:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/15529.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/15529.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/15529.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/15529.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/15529.html</trackback:ping><description><![CDATA[
		<p>
				<strong>
						<font face="宋体">摘要：</font>
				</strong>介绍了在Iinux系统下开发符合Video for Linux标准的USB摄像头驱动的方法，并对该标准提出“不间断采集”的改进思路，配合双URB、双帧缓冲等方法，提高采集速度。</p>
		<p class="MsoNormal">
				<span style="font-family: 宋体;" times="" mso-hansi-font-family="" ?times="" new="" roman="">    <b>关键词：</b></span>Linux设备驱动 USB摄像头 Video for Linux 不间断采集</p>
		<p style="text-indent: 30px;">USB摄像头以其良好的性能和低廉的价格得到广泛应用。同时因其灵活、方便的特性，易于集
成到嵌入式系统中。但是如果使用现有的符合Video for
Linux标准的驱动程序配合通用应用程序，难以充分利用USB带宽，帧速不高，不易满足实时监控等要求。本文首先介绍在Linux系统下USB摄像头驱
动编制的一般方法，然后说明在此基础上如何提高帧速。</p>
		<p style="text-indent: 0px;">
				<b>1 Linux系统中的USB摄像头驱动程序</b>
		</p>
		<p style="text-indent: 30px;">USB设备驱动程序完全符合通用设备驱动的准则，不同的是内核提供了一些特别的API函数，方便驱动注册、销毁自己，例如usb_reSister()和usb_dereSister()；2．4版的内核还提供了对于hotplug的支持。</p>
		<p style="text-indent: 30px;">1．1 USB摄像头驱动的一般编写方法</p>
		<p style="text-indent: 30px;">摄像头属于视频类设备。在目前的Linux核心中，视频部分的标准是Video for
Linux(简称V4L)。这个标准其实定义了一套接口，内核、驱动、应用程序以这个接口为标准进行交流。目前的V4L涵盖了视、音频流捕捉及处理等内
容，USB摄像头也属于它支持的范畴。</p>
		<p style="text-indent: 30px;">因此，USB摄像头的驱动应当与内核提供的视频驱动挂钩。即首先在驱动中声明一个
video_device结构，并为其指定文件操作函数指针数组．fops，向系统注册。在应用程序发出文件操作的相关命令时，核心根据这些指针调用相应
函数，并将该结构作为参数传递给它们。这样，就完成了驱动和核心之间的通信。例如：<a href="http://www.embeded.cn/home/21ic/2006022322453507.gif" target="_blank"><img src="http://www.embeded.cn/home/21ic/2006022322453507.gif" title="点击在新窗口查看原始图片" onload="java_script_:if(this.width&gt;300)this.width=300" align="right" border="0" height="266" hspace="1" vspace="1" width="199" /></a></p>
		<p style="text-indent: 30px;">static struct video_device vdev_template＝{……}；</p>
		<p style="text-indent: 30px;">／／声明video_device，指出挂接驱动</p>
		<p style="text-indent: 30px;">static struct file_operations ov511_fops＝{……}；</p>
		<p style="text-indent: 30px;">／／声明本驱动的文件操作函数指针</p>
		<p style="text-indent: 30px;">struct video_device*vdev＝video_devdata(file)；</p>
		<p style="text-indent: 30px;">／／从文件指针中提取出video_device结构</p>
		<p style="text-indent: 30px;">在video_device结构中，有一个私有指针priv，可以将它指向一块保留内存。
在这块内存中，保存着本驱动、本设备的相关初始化信息。这块内存的申请、初始化、指针指向等工作都是在USB驱动的枚举函数.probe中完成。这样，在
枚举函数将控制权返还给系统后，因为内核不销毁保留内存，所以驱动仍然保留着自己的信息。这点与Windows系统中WDM驱动有异曲同工之处。当然，在
驱动卸载函数中，应当将申请的各块内存全部释放。</p>
		<p style="text-indent: 30px;">1．2 使用双URB轮流通信</p>
		<p style="text-indent: 30px;">众所周知，USBl．1总线标准定义了控制、中断、批量、等时等四种管道。对于时间性极强但是准确度要求不高的视频捕捉应用来说，摄像头应当使用等时传输方式。为了尽可能快地得到图像数据，应当在URB中指定USB_ISO_ASAP标志。</p>
		<p style="text-indent: 30px;">urb-&gt;transfer_flags＝USB_ISO_ASAP；／／尽可能快地发出本URB</p>
		<p style="text-indent: 30px;">Linux系统中任何USB传输都通过URB实现。为提高速度，可以考虑扩大URB的缓
冲，这样可以降低每个USB事务中握手信息所占比例，提高有效数据的传输速度。但是受限于总线带宽和具体的USB设备芯片，单纯扩大URB的缓冲不能无限
制地解决问题。具体分析一下USB传输在操作系统中的实现：每次传输都要包括URB的建立、发出、回收、数据整理等阶段，这些时间不产生有效数据。因此可
以建立两个URB，在等待一个URB被回收时，也就是图像正在被传感器采集时，处理、初始化另一个URB，并在回收后立刻将其发出。两个URB交替使用，
大大减少了额外时间。工作流程如图1所示。</p>
		<p style="text-indent: 30px;">这个过程是在URB的完成例程中实现的，有两点需要注意：首先处理再次初始化的代码时间不
能长，否则会造成完成例程的重人，如果确实来不及，可以在完成例程中设定标志，例如“数据采集好”旗语，由应用程序使用阻塞ioctl()来查询该旗语并
做处理；其次由于CPU可能会在完成例程中停留较长时间，系统负担较大，可以在．open函数中初始化两个URB并将其发出，有限度地减轻系统负担。</p>
		<p style="text-indent: 30px;">1．3 使用双帧缓冲提高效率</p>
		<p style="text-indent: 30px;">Linux系统中，文件操作通常是由read、write等系统调用来完成。这些系统调用
在驱动中的解决方法就是用copy_to_user()、copy_from_user()等函数在核态、户态内存空间中互相拷贝。但是对于大批量的图像
数据，采用拷贝的方法显然会增加时间开销，因此用内存映射的方法解决。首先使用vmalloc()申请足够大的核态内存，将其作为图像数据缓冲空间，两个
URB带回的图像数据在这里暂存；然后使用remap_page_range()函数将其逐页映射到用户空间中。户态的图像处理程序使用mmap()函
数，直接读写核态图像缓冲内存，大大减少额外开销。<br /><br /><b><a href="http://www.embeded.cn/info/images/aet/200411/3b.gif">图2</a></b><br /><br /><font size="3">    </font><span class="main1">图像数据的处理可能要花费比较长的时间，不同的算法对于数据保留时间的要求也不一样。因此可以申请两帧图像缓冲，在处理一帧图像的同时，将两个URB带回的数据全部填充到另一帧缓冲中，这样可以免去时间冲突上的麻烦。</span></p>
		<p style="text-indent: 30px;">值得注意的是：这种方法要求时刻持有当前帧的序号、每一帧的起始地址等信息，不能将两帧图像混淆。这些信息可以保存在保留内存中，当前帧的数据整理、序号改变在URB完成例程中实现。</p>
		<p style="text-indent: 0px;">
				<b>2 V4L标准的改进</b>
		</p>
		<p style="text-indent: 30px;">V4L标准目前已经发展到第二版V4L2，其基本思路与V4L相同。</p>
		<p style="text-indent: 30px;">2．1 标准分析</p>
		<p style="text-indent: 30px;">根据V4L标准，户态程序在需要一帧图像时，CPU的走向如图2。CPU按照123456
的顺序完成一个循环。在这里，有一个细节被忽略：在完成例程中，也就是图2中步骤6，该URB被立刻发出，但是由于这时用户程序正在阻塞等待，没办法再次
提出获得图像的申请，因此在判断有无新请求时，判断的结果必然是当前无请求，导致下一个URB带回的数据被驱动丢弃；由于核态、户态的切换需要一定的时
间，加上户态多进程同步等开销，等到应用程序能够再次发出获得一副图像的申请时，已经有不止一个URB带回的数据被丢弃掉，这些URB包含的数据正好是新
一帧图像的开始部分。因此驱动必须等到再下一帧图像才能保存数据、缓冲。这样凭白损失了一帧图像，帧速最少下降一半。</p>
		<p style="text-indent: 30px;">2．2 改进思路：不间断采集</p>
		<p style="text-indent: 30px;">为了解决这个问题，可以改进V4L标准作，使其增加新的功能：通过新的参数，让ioetl
()函数通知驱动不间断采集、缓冲图像数据，轮流保存在两帧缓冲区中，并在一帧图像采集好后，设定“图像采集好”旗语。户态程序只需要发出一次“获得图
像”请求，就可以通过阻塞等待该旗语，不断获得图像。在采集结束后，再次通过新的参数，让驱动停止缓冲即可。CPU工作流程图如图3。<br /><br /><b><a href="http://www.embeded.cn/info/images/aet/200411/3c.gif">图3</a></b><br /><br /><font size="3">    </font><span class="main1">注意到图2、图3，两种“判断有无新请求”的不同，即可发现新方法假定一直有请求，因此不丢弃每个URB带回的数据，轮流保存在两个帧缓冲内。</span></p>
		<p style="text-indent: 30px;">V4L已经作为约定俗成的标准被内核支持，因此如果使用全新的参数，工作量将相当巨大，并
且不能和现有的应用程序兼容。考虑到现有的图像采集应用程序使用VIDIOCMCAPTURE作为参数，并提供帧序号，要求驱动将图像保存到指定序号的帧
缓冲内。由于驱动通常仅仅提供几帧缓冲，因此该序号不会大于某个数字，如10。因此可以继续使用VIDIOCMCAPTURE参数，搭配较大的序号来表示
新增的功能，例如用10000和10001来分别表示开始和停止缓冲图像数据的要求。驱动在收到VIDIOCMCAPTURE要求后，检查这个序号。如果
小于10000，则按照正常的方法处理，否则按照改进方法。这种思路可以有效解决兼容性问题。</p>
		<p style="text-indent: 30px;">2．3 实验结果</p>
		<p style="text-indent: 30px;">在赛扬366、USBl．1接口的计算机平台上，采用上述不间断采集改进V4L标准，配合双URB、双帧缓冲等方法后，帧速提高两倍有余，有效数据传输速度达960KB／s，接近等时传输方式下USB总线的带宽极限。</p>
		<br />
<img src ="http://www.cnitblog.com/zouzheng/aggbug/15529.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-08-18 21:06 <a href="http://www.cnitblog.com/zouzheng/articles/15529.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>YAFFS文件系统移植笔记</title><link>http://www.cnitblog.com/zouzheng/articles/15522.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Fri, 18 Aug 2006 12:48:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/15522.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/15522.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/15522.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/15522.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/15522.html</trackback:ping><description><![CDATA[
		<p>
				<font color="#333333">基于Linux2.6的YAFFS文件系统移植<br />v1.0,2005-6-6<br />一、YAFFS文件系统简介<br />YAFFS，Yet Another Flash File System，是一种类似于JFFS/JFFS2的专门为Flash设计的嵌入式文件系统。与JFFS相比，它减少了一些功能，因此速度更快、占用内存更少。<br />YAFFS和JFFS都提供了写均衡，垃圾收集等底层操作。它们的不同之处在于：<br />（1）、JFFS是一种日志文件系统，通过日志机制保证文件系统的稳定性。YAFFS仅仅借鉴了日志系统的思想，不提供日志机能，所以稳定性不如JAFFS，但是资源占用少。<br />（2）、JFFS中使用多级链表管理需要回收的脏块，并且使用系统生成伪随机变量决定要回收的块，通过这种方法能提供较好的写均衡，在YAFFS中是从头到尾对块搜索，所以在垃圾收集上JFFS的速度慢，但是能延长NAND的寿命。<br />（3）、JFFS支持文件压缩，适合存储容量较小的系统；YAFFS不支持压缩，更适合存储容量大的系统。<br />YAFFS
还带有NAND芯片驱动，并为嵌入式系统提供了直接访问文件系统的API，用户可以不使用Linux中的MTD和VFS，直接对文件进行操作。NAND
Flash大多采用MTD+YAFFS的模式。MTD（ Memory Technology
Devices，内存技术设备）是对Flash操作的接口，提供了一系列的标准函数，将硬件驱动设计和系统程序设计分开。<br />二、YAFFS文件系统的移植<br />yaffs代码可以从<a href="http://www.aleph1.co.uk/armlinux/projects/">http://www.aleph1.co.uk/armlinux/projects/</a>下载（yaffs代码包括yaffs_ecc<br />.c，yaffs_fileem.c，yaffs_fs.c，yaffs_guts.c，yaffs_mtdif.c，yaffs_ramem.c。）<br />表一 Yaffs文件系统源代码相关文件及功能描述<br />文件名 功   能<br />yaffs_ecc.c ECC校验算法<br />yaffs_fileem.c 测试flash<br />yaffs_fs.c 文件系统接口函数<br />yaffs_guts.c Yaffs文件系统算法<br />yaffs_mtdif.c NAND函数<br />yaffs_ramem.c Ramdisk实现<br />1.内核中没有YAFFS，所以需要自己建立YAFFS目录，并把下载的YAFFS代码复制到该目录下面。<br />#mkdir fs/yaffs<br />#cp *.c(yaffs source code) fs/yaffs<br />2.修改fs/Kconfig，使得可以配置yaffs :<br />source "fs/yaffs/Kconfig"<br />3．修改fs/makefile，添加如下内容：<br />obj-$(CONFIG_YAFFS_FS)          += yaffs/<br />4.在fs目录下生成yaffs目录，并在里面生成一个makefile 和Kconfig<br />Makefile 内容为：<br />yaffs-objs := yaffs_fs.o yaffs_guts.o yaffs_mtdif.o yaffs_ecc.o <br />EXTRA_CFLAGS += $(YAFFS_CONFIGS) -DCONFIG_KERNEL_2_6<br />Kconfig内容为：<br />#<br /># YAFFS file system configurations<br />#<br />config YAFFS_FS<br /> tristate "Yet Another Flash Filing System(YAFFS) file system support"<br /> help<br />   YAFFS, for Yet Another Flash Filing System, is a filing system<br />   optimised for NAND Flash chips.</font>
		</p>
		<p>
				<font color="#333333">   To compile the YAFFS file system support as a module, choose M here:<br />   the module will be called yaffs.</font>
		</p>
		<p>
				<font color="#333333">   If unsure, say N.</font>
		</p>
		<p>
				<font color="#333333">   Further information on YAFFS is available at<br />   &lt;<a href="http://www.aleph1.co.uk/yaffs/">http://www.aleph1.co.uk/yaffs/</a>&gt;.</font>
		</p>
		<p>
				<font color="#333333">config YAFFS_MTD_ENABLED<br /> bool "NAND mtd support"<br /> depends on YAFFS_FS<br /> help<br />   This adds the yaffs file system support for working with a NAND mtd.</font>
		</p>
		<p>
				<font color="#333333">   If unsure, say Y.</font>
		</p>
		<p>
				<font color="#333333">config YAFFS_RAM_ENABLED<br /> bool "yaffsram file system support"<br /> depends on YAFFS_FS<br /> help<br />   This adds the yaffsram file system support. Nice for testing on x86,<br />   but uses 2MB of RAM.  Don't enable for NAND-based targets.</font>
		</p>
		<p>
				<font color="#333333">   If unsure, say N.</font>
		</p>
		<p>
				<font color="#333333">comment "WARNING: mtd and/or yaffsram support should be selected"<br /> depends on YAFFS_FS &amp;&amp; !YAFFS_MTD_ENABLED &amp;&amp; !YAFFS_RAM_ENABLED</font>
		</p>
		<p>
				<font color="#333333">config YAFFS_USE_OLD_MTD<br /> bool "Old mtd support"<br /> depends on YAFFS_FS &amp;&amp; 0<br /> help<br />   Enable this to use the old MTD stuff that did not have yaffs support.<br />   You can use this to get around compilation problems, but the best<br />   thing to do is to upgrade your MTD support. You will get better speed.</font>
		</p>
		<p>
				<font color="#333333">   If unsure, say N.</font>
		</p>
		<p>
				<font color="#333333">config YAFFS_USE_NANDECC<br /> bool "Use ECC functions of the generic MTD-NAND driver"<br /> depends on YAFFS_FS<br /> default y<br /> help<br />   This enables the ECC functions of the generic MTD-NAND driver.<br />   This will not work if you are using the old mtd.</font>
		</p>
		<p>
				<font color="#333333">   NB Use NAND ECC does not work at present with yaffsram.</font>
		</p>
		<p>
				<font color="#333333">   If unsure, say Y.</font>
		</p>
		<p>
				<font color="#333333">config YAFFS_ECC_WRONG_ORDER<br /> bool "Use the same ecc byte order as Steven Hill's nand_ecc.c"<br /> depends on YAFFS_FS<br /> help<br />   This makes yaffs_ecc.c use the same ecc byte order as<br />   Steven Hill's nand_ecc.c. If not set, then you get the<br />   same ecc byte order as SmartMedia.</font>
		</p>
		<p>
				<font color="#333333">   If unsure, say N.</font>
		</p>
		<p>
				<font color="#333333">config YAFFS_USE_GENERIC_RW<br /> bool "Use Linux file caching layer"<br /> default y<br /> depends on YAFFS_FS<br /> help<br />   Use generic_read/generic_write for reading/writing files. This<br />   enables the use of the Linux file caching layer.</font>
		</p>
		<p>
				<font color="#333333">   If you disable this, then caching is disabled and file read/write<br />   is direct.</font>
		</p>
		<p>
				<font color="#333333">   If unsure, say Y.</font>
		</p>
		<p>
				<font color="#333333">config YAFFS_USE_HEADER_FILE_SIZE<br /> bool "Use object header size"<br /> depends on YAFFS_FS<br /> help<br />   When the flash is scanned, two file sizes are constructed:<br />   * The size taken from the object header for the file.<br />   * The size figured out by scanning the data chunks.<br />   If this option is enabled, then the object header size is used,<br />   otherwise the scanned size is used.</font>
		</p>
		<p>
				<font color="#333333">   If unsure, say N.</font>
		</p>
		<p>
				<font color="#333333">config YAFFS_DISABLE_CHUNK_ERASED_CHECK<br /> bool "Turn off debug chunk erase check"<br /> depends on YAFFS_FS<br /> default y<br /> help<br />   Enabling this turns off the test that chunks are erased in flash<br />   before writing to them.  This is safe, since the write verification<br />   will fail.  Suggest enabling the test (ie. say N)<br />   during development to help debug things.</font>
		</p>
		<p>
				<font color="#333333">   If unsure, say Y.</font>
		</p>
		<p>
				<font color="#333333">#config YAFFS_DISABLE_WRITE_VERIFY<br /># bool "Disable write verify (DANGEROUS)"<br /># depends on YAFFS_FS &amp;&amp; EXPERIMENTAL<br /># help<br />#   I am severely reluctant to provide this config. Disabling the<br />#   verification is not a good thing to do since NAND writes can<br />#   fail silently.  Disabling the write verification will cause your<br />#   teeth to rot, rats to eat your corn and give you split ends.<br />#   You have been warned. ie. Don't uncomment the following line.<br />#<br />#   If unsure, say N.<br />#</font>
		</p>
		<p>
				<font color="#333333">config YAFFS_SHORT_NAMES_IN_RAM<br /> bool "Cache short names in RAM"<br /> depends on YAFFS_FS<br /> default y<br /> help<br />   If this config is set, then short names are stored with the<br />   yaffs_Object.  This costs an extra 16 bytes of RAM per object,<br />   but makes look-ups faster.</font>
		</p>
		<p>
				<font color="#333333">   If unsure, say Y.<br />5．在/arch/arm/mach-s3c2410/mach-smdk2410.c找到smdk_default_nand_part结构，修改nand分区，如下：<br />struct mtd_partition smdk_default_nand_part[] = {<br />        [0] = {<br />                .name   = "vivi",<br />                .size   = 0x00020000,<br />                .offset = 0x00000000,<br />        },<br />        [1] = {<br />                .name   = "param",<br />                .size   = 0x00010000,<br />                .offset = 0x00020000,<br />        },<br />        [2] = {<br />                .name   = "kernel",<br />                .size   = 0x00100000,<br />                .offset = 0x00030000,<br />        },<br />        [3] = {<br />                .name   = "root",<br />                .size   = 0x01900000,<br />                .offset = 0x00130000,<br />        },<br />        [4] = {<br />                .name   = "user",<br />                .size   = 0x025d0000,<br />                .offset = 0x01a30000,<br />        }<br />};<br />注：此分区要结合vivi里面的分区来进行设置。<br />6．配置内核时选中MTD支持：<br />Memory Technology Devices (MTD)  ---&gt;<br /> &lt;*&gt; Memory Technology Device (MTD) support<br />  [*]   MTD partitioning support<br />   ……<br />  --- User Modules And Translation Layers<br />  &lt;*&gt; Direct char device access to MTD devices<br />  &lt;*&gt; Caching block device access to MTD devices<br />  ……<br />NAND Flash Device Drivers  ---&gt;<br /> &lt;*&gt; NAND Device Support<br /> &lt;*&gt; NAND Flash support for S3C2410 SoC<br /> [*]   S3C2410 NAND driver debug  <br />7．配置内核时选中YAFFS支持：<br />File systems  ---&gt;<br /> Miscellaneous filesystems  ---&gt;<br />  &lt;*&gt; Yet Another Flash Filing System(YAFFS) file system support<br />  [*]   NAND mtd support<br />  [*]   Use ECC functions of the generic MTD-NAND driver<br />  [*]   Use Linux file caching layer<br />  [*]   Turn off debug chunk erase check<br />  [*]   Cache short names in RAM<br />8.编译内核并将内核下载到开发板的flash中。<br />三、Yaffs文件系统测试：<br />1.内核启动之后，在启动信息里面可以看到如下内容：<br />NAND device: Manufacturer ID: 0xec, Chip ID: 0x76 (Samsung NAND 64MiB 3,3V 8-bit)<br />Scanning device for bad blocks<br />Creating 5 MTD partitions on "NAND 64MiB 3,3V 8-bit":<br />0x00000000-0x00020000 : "vivi"<br />0x00020000-0x00030000 : "param"<br />0x00030000-0x00130000 : "kernel"<br />0x00130000-0x01a30000 : "root"<br />0x01a30000-0x04100000 : "user"<br />2.如果在内核里面添加了proc文件系统的支持那么你在proc里面可以看到有关yaffs的信息<br />~ # cat proc/filesystems<br />nodev   sysfs<br />nodev   rootfs<br />nodev   bdev<br />nodev   proc<br />nodev   sockfs<br />nodev   pipefs<br />nodev   futexfs<br />nodev   tmpfs<br />nodev   eventpollfs<br />nodev   devpts<br />nodev   ramfs<br />        vfat<br />nodev   devfs<br />nodev   nfs<br />        yaffs<br />nodev   rpc_pipefs<br />3.查看dev目录下相关目录可以看到：<br />~ # ls dev/mtd -al<br />drwxr-xr-x    1 root     root            0 Jan  1 00:00 .<br />drwxr-xr-x    1 root     root            0 Jan  1 00:00 ..<br />crw-rw-rw-    1 root     root      90,   0 Jan  1 00:00 0<br />cr--r--r--    1 root     root      90,   1 Jan  1 00:00 0ro<br />crw-rw-rw-    1 root     root      90,   2 Jan  1 00:00 1<br />cr--r--r--    1 root     root      90,   3 Jan  1 00:00 1ro<br />crw-rw-rw-    1 root     root      90,   4 Jan  1 00:00 2<br />cr--r--r--    1 root     root      90,   5 Jan  1 00:00 2ro<br />crw-rw-rw-    1 root     root      90,   6 Jan  1 00:00 3<br />cr--r--r--    1 root     root      90,   7 Jan  1 00:00 3ro<br />crw-rw-rw-    1 root     root      90,   8 Jan  1 00:00 4<br />cr--r--r--    1 root     root      90,   9 Jan  1 00:00 4ro</font>
		</p>
		<p>
				<font color="#333333">~ # ls dev/mtdblock/ -al<br />drwxr-xr-x    1 root     root            0 Jan  1 00:00 .<br />drwxr-xr-x    1 root     root            0 Jan  1 00:00 ..<br />brw-------    1 root     root      31,   0 Jan  1 00:00 0<br />brw-------    1 root     root      31,   1 Jan  1 00:00 1<br />brw-------    1 root     root      31,   2 Jan  1 00:00 2<br />brw-------    1 root     root      31,   3 Jan  1 00:00 3<br />brw-------    1 root     root      31,   4 Jan  1 00:00 4<br />4.mount、umount<br />建立mount目录<br />~ #mkdir /mnt/flash0<br />~ #mkdir /mnt/flash1<br />Mountblockdevice设备<br />~ #mount –t yaffs /dev/mtdblock/3 /mnt/flash0<br />~ #mount –t yaffs /dev/mtdblock/4 /mnt/flash1<br />~ #cp 1.txt /mnt/flash0<br />~ #cp 2.txt /mnt/flash1<br />查看mount上的目录，可以看到该目录下有刚才拷贝的文件，将其umount后，再次mount上来可以发现拷贝的文件仍然存在，这时删除该文件然后umount，再次mount后，可以发现拷贝的文件已经被删除，由此可以该分区可以正常读写。<br />5.在flash上建立根文件系统<br />~ # mount –t yaffs /dev/mtdblock/3 /mnt/flash0<br />~ #cp (your rootfs) /mnt/flash0<br />~ #umount /mnt/flash0<br />重新启动，改变启动参数：<br />param set linux_cmd_line "noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0"<br />重新启动，开发板就可以从flash启动根文件系统了。<br />注：这里你得在内核中添加devfs文件系统的支持，否则内核无法找到/dev/mtdblock/3目录</font>
		</p>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/15522.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-08-18 20:48 <a href="http://www.cnitblog.com/zouzheng/articles/15522.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>