` 感冒了!浑身乏力!郁闷死了! 先来点感想,我们老老实实写篇文章的过程,也是自己的重新思考的过程。查资料的过程也是个很好的学习过程,有些头脑里面似是而非的东西,会在这个过程中得到整理,强化。这样强化后的知识才能真正成为自己的知识。 所以管理员们催稿,不是在逼你,而是在督促你学习,进步! 反之,随随便便水一篇文章。力气是省了,拼拼凑凑就完事了。但是文章提交后,也就结束了,你大脑里还是一片空白,的知识点还是一团浆糊。 但是你想过没有,凑水文也要花时间的,时间花掉了。你学到东西了吗? 经常有人问:“我的时间去哪了? 其实,你自己才是偷走你时间的最大的贼。 总之,赔钱的买卖咱不干,还是老老实实写文章,学知识来得实在。 一、裸机程序运行准备工作 这次咱们就来看看裸机程序到底是怎样跑起来的。 从上次的文章我们可以看到,机器是如何引导起来的。 还记得上一篇文章里,我说的记住那些红色标记的地址了吗? 现在用到了那些标记过的地址了,我们再把这个图再借过来放这儿,方便查看。 parsing./android/partmap.txt: part.0flash=mmc,0:2ndboot: 2nd: 0x200,0x8e00: :[RAW] part.1 flash=mmc,0:bootloader:boot: 0x8000,0x77000: :[RAW] part.2flash=mmc,0:boot: fat: 0x100000,0x4000000: boot.img :[MBR] ./android/boot.img part.3flash=mmc,0:system: ext4: 0x4100000,0x2f200000: system.img :[MBR] ./android/system.img part.4flash=mmc,0:cache: ext4: 0x33300000,0x1ac00000: cache.img :[MBR] ./android/cache.img part.5flash=mmc,0:misc: emmc: 0x4e000000,0x800000: :[MBR] part.6flash=mmc,0:recovery: emmc: 0x4e900000,0x1600000: :[MBR] part.7flash=mmc,0:userdata: ext4: 0x50000000,0x0: userdata.img :[MBR]./android/userdata.img 这次,我们还是借助于SD卡来引导我们的小pi2。 我们在这里要对SD卡来个“乾坤大挪移”,采取“偷梁换柱”的手法来实施我们的计划。 那有人要问了:偷那个梁,换那个柱呢? 理论上,在友善官方提供的img中我们可以替换的地方有2个,一个是三星提供的2ndboot处,另外一个是友善提供的uboot处,替换掉这两个地方应该都能达到运行我们的裸机程序的目的。 个人猜测,cpu运行到这两个地方的后,现场的环境应该是不一样的,具体有哪些不同,要看这2个不同阶段的boot程序了。 这里面有一个问题要提前提醒你,那就是如果你的裸机程序要是替换2ndboot,那么你的裸机程序不能超过16k,否则,产生意想不到的后果。 原因如下: 下面再来看张图图吧! 老让你看图有点烦哈!没办法,我文字表达能力太差。还是看图说得明白
没看懂也没关系,只要记住一点,我们自己写的替换2ndboot的程序不能大于16k, 为什么,因为三星规定死了。 有较真的又要问了,我就要大于16k怎么办? 哎,愁人!办法也是有的,就是你要把自己写的程序分成2段,分别加载,当然所有的初始化要你自己做好。并且要你自己在的程序里,处理运行环境程序初始化(cpu、内存)、复制、转移等等工作。 现在该知道怎么偷天换日了吧。 对了,就使用我们的程序替换掉2ndboot,让internalrom把我们的程序当成2ndboot,调入到sram里执行,从而达到我们的程序运行的目的。 好了现在终于到了准备一段程序来练练手的时候了? 二、点亮led程序的原理及程序 我们的重点在于讲清楚程序运行的原理,而不是程序的复杂性。所以能简单就简单点,高大上的程序要靠您来完成了。 就拿“点灯大法”来练练手吧! 先说说点灯大法的原理,不知道有多少高手都是从这里走出来的。 我们这次就拿这种直径 3mm 插件led灯珠来说说点亮的原理吧! 首先,看看led的发光原理 LED(Light Emit ting Diode),发光二极管,是一种能够将电能转化为可见光的固态的 半导体器件,它可以直接把电转化为光。LED的心脏是一个半导体的晶片,晶片的一端附在一个支架上,一端是负极,另一端连接 电源的正极,使整个晶片被环氧树脂封装起来。半导体晶片由两部分组成,一部分是P型半导体,在它里面空穴占主导地位,另一端是N型半导体,在这边主要是 电子。但这两种半导体连接起来的时候,它们之间就形成一个P-N结。当电流通过导线作用于这个晶片的时候,电子就会被推向P区,在P区里电子跟空穴复合,然后就会以光子的形式发出能量,这就是LED灯发光的原理。而光的波长也就是光的颜色,是由形成P-N结的材料决定的。 看明白了吗?就是要给led通过电流。 借用我们的小pi2的user led说明一下:
那怎么通过电流? 给led两端加电压呀! 聪明,就是在led灯珠的两个管脚加上电压。让左右两边产生电压差,电流就产生了,于是Led就发光了。 好,旁边一个聪明的兄弟来精神了,加电压吗?太方便了,马上直接整个220给加上去了。 结果你肯定能猜测到了。“机毁人亡”。 对了,刚才忘了提醒一下了,我们采用的这种小Led的他的工作电流很小,用不了220v这头大牛! 常见的小led的参数如下: 直径 3mm 插件LED: 额定电流都是 20 mA,使用是电流 15 mA。 红黄灯珠电压是 1.8V-2.2V, 蓝绿白灯珠电压是 3.0V-3.6V。 直径 5mm 插件LED 工作电流是10mA, 电压1.6V左右。 因为工作电流小,点亮电压低,用我们的pi2的gpio可以直接点亮它,不需要外加任何驱动。 知道我为什么要选这种led做实验了吧! 大概解释一下,这个原理图: 板子上电后,电源管理芯片开始工作,于是vdd_sys_3.3v开始出现,对于pwr led ,左端3.3v,右端接地,左右两端形成电流,于是开始发光。这就是问什么我们接上供电后,电源指示灯,就一直发光的原因。 而对于stat led ,情况就不同了,如果led1引脚在这时被送过来3.3v的电压,led等左右两边无法形成电压差,也就没有电流产生,也就不会发光。 反过来说,如果我们给led1针脚送去不同周期的高低电平,此led就会交替闪烁。 这就是我们控制led灯发光的基本原理。 Ok。 喝口水,回来继续。 好了, 再看看原理图,我们拿板载的led为例,来验证咱们前面的分析,看看正确与否。
从这两个图上不难看出,我们的Led1连接在gpiob12上,根据前面的分析,我们只要给这个引脚放置一个低电平,就可以让这个led1点亮了。如果给它让这个电平出现周期性的变化,那么这个led1也就跟着发生周期性的明暗交替变化。 老规矩,先看原理图。
注意不同gpio口的基地址,和需要设置的寄存器功能。 我们这次主要是设置gpio的B口,输出高低电平。 因此,主要看和B口控制寄存器和地址寄存器相关的设置内容就可以了。 我们需要gpio作为输出功能用,因此,看看15.3.2ououtput operation这节。
再看看gpio映射的地址
Ok,根据以前编写单片机程序的经验,我们的裸机程序需要有头文件,c语言源文件。 根据以往的ARM芯片的编程经验,我们的裸机程序,比单片机程序还要多增加一个汇编语言源文件作为程序的入口。 下面卡是准备程序: 为了方便,我参考了网友“哀其不幸”的程序,根据我们的小pi2,略作修改。 需要3个程序: 一个启动启动的汇编程序start.s,一个头文件gpio.h,一个主程序main.c。 源程序如下: Start.s b start .word 0x00000000 .word 0x00000000 .word 0x00000000 .word 0x00000000 .word 0x00000000 .word 0x00000000 .word 0x00000000 start: b main gpio.h #defineGPIOBOUT (*(volatile unsigned *) 0xc001b000) //GPIOB引脚输出电平设置 #defineGPIOBOUTENB (*(volatile unsigned *)0xc001b004) //GPIOB引脚IO模式选择,0-输入模式,1-输出模式 #defineGPIOBALTFN0 (*(volatile unsigned *)0xc001b020) //GPIOB(0-15)引脚功能选择,00-普通IO功能 Main.c #include"gpio.h" #defineLED1ON GPIOBOUT |= 0x01 #defineLED1OFF GPIOBOUT &= ~(0x1<<0) voiddelay(unsigned int count) { while(count--); } voidmain(void) { /*初始化GPIOB*/ GPIOBALTFN0 &=~((0x3<<0)|(0x3<<2)|(0x3<<4)); GPIOBOUTENB |= 0x07; GPIOBOUT &= ~(0x7); while(1) { LED1ON; delay(2000000); LED1OFF; } } 分别建立这3个文件后。 单独编译他们。 arm-linux-gcc -c start.s -o start.o arm-linux-gcc -c main.c -o main.o arm-linux-ld -Ttext=0x42C00000 start.omain.o -o main arm-linux-objcopy -I elf32-littlearm -Obinary main main.bin 有眼睛尖的同学肯定要问了,你这个0x42C00000怎么来的。为什么要选这个地址。 好,听我慢慢道来。 我们先不烧写程序,先来做个试验。 就是先做个启动系统的最小化sd卡。 这张卡里面只包括:2ndboot镜像。 三、单独启动pi2的系统的最小化的镜像sd卡制作。 制作sd卡的工具有很多,我们用广受好评的winhex来做吧。这个工具在数据恢复领域使用很广。不知有多少死亡的数据因为它儿起死回生。不知挽救了多少粗心大意的网管的命。 首先用winhex打开友善官方镜像如下图:
alt+g 输入200,(注意后面的十六进制,十进制的切换方法·是偺offset处点击鼠标。) 点击确定,在光标闪烁的位置,点击右键,alt+1, 选中要复制的起始块位置, 重复上面的alt+g 输入8200,
点击右键,alt+2, 选中要复制的结束块位置,(注意此处选择81ff处作为结束块。) 右键点击 编辑 –》复制选块-》十六进制数值。
此时0x200-0x81ff的数据已经进入到剪贴板里面。 接下来, 在winhex顶部打开 工具-》打开磁盘-》选中我们已经彻底格式化的sd卡。
点击确定 同样的方法走到,sd卡的0x200偏移处。
右键点击 编辑-》剪贴板数据-》写入-》确定。
回到winhex顶部,点击保存,把改变的数据写入sd卡。 到此,我们能启动pi2的最小化系统只做好了。 比dos时代的那个sysc: 复杂多了吧! 难怪懂嵌入式系统的人越来越少了呢? 原来是工具越来越复杂了。 好了,把这张卡插入我们的小pi2。Stop,stop,stop。先别急着通电 把串口连接到我们的主电脑,看看串口有什么鬼再说吧。 什么?为什么要用串口,因为,没有别的调试方法啊! 我们的小pi2太小了,没有引出任何调试接口啊! 好了,开机,看看串口有什么东东输出来没有。
全部的信息如下: -------------------------------------------------------------------------------- Second Boot byNexell Co. : Built on Nov 19 2014 20:10:38 -------------------------------------------------------------------------------- PLL0: 550000000PLL1: 1200000000 PLL2: 800000000 PLL3: 612000000 Divider0 PLL: 1CPU:1200000000 CPU BUS:300000000 Divider1 PLL: 2BCLK:400000000 PCLK:200000000 Divider2 PLL: 2MDCLK:800000000 MCLK:800000000 MBCLK:400000000MPCLK:200000000 Divider3 PLL: 0G3D BCLK:550000000 Divider4 PLL: 2MPEG BCLK:400000000 MPEG PCLK:200000000 123 DDR3 POR InitStart phy init ##########READ/GATE Level ########## DDR3 Init Done! Loading fromsdmmc... Image LoadingDone! Launch to0x42C00000 从串口输出的信息看,我们确实是运行在2ndboot里面了。 0x42C00000这个地址要记住,如果我们的裸机测试程序,需要用友善的2nduboot引导,那么链接时要指向这个地址。 好了,这回知道我们为什么要把上面程序的链接地址指向这里了吧! 这样保证我们的程序在2ndboot交出控制权后,我们的程序能顺利执行。 四、用2ndboot+裸机程序烧sd卡,引导pi2的测试 好了,把编译过的裸机程序main.bin放到sd卡上去吧。 位置就是我们uboot存放的位置处,偏移地址0x8200的后面。 操作过程一样,就是打开的文件是main.bin,全部复制main.bin上的所有文件,十六进制复制,sd卡偏移起始地址选择0x8200.最后别忘了存盘。 再次用这个2ndboot+main.bin制作的sd卡引导pi2吧! 果然不出所料,我们的小pi2终于开始闪烁着迷人的蓝色光芒了。 怎一个爽字得了! 结束了,回家吃饭! 别的! 还没完呢? 我们还没测试用main.bin 替代2ndboot后能不能运行我们的逻辑程序呢! 革命尚未完全成功,通知仍需努力! 五、用main.bin 替代2ndboot引导我们的小pi2的测试 用我们的裸机程序代替2ndboot,看看运行效果如何。 Ok,首先先彻底格式化sd卡,插到小pi2上看看串口输出结果。
有看官说了,大哥你别逗了!哪里有显示啊! 没上显示就对了,因为还没有程序运行呢? internalrom 抓不到东西运行啊! 个人猜测,土财主三星没有在internal rom里面写串口通讯程序,所以串口没法显示。 不过能理解三星也不知道我们的小pi2要用哪几个口做串口通讯用啊! 所以也就没办法写这个程序了。 这样解释你该满意了吧。 (nnd,我第一次实验的时候在这个地方卡了2天多,一直以为是裸机程序有问题,那个汗哪。。。。。。) 既然如此,那是不是就不能做实验了呢? No,no,no,既然internal rom 能把2ndboot搬移到sram里面执行,那我把我的裸机程序放在2ndboot的位置代替它,没有理由不被internal rom给搬到iram里面去运行啊! 行不行的,试试再说。 说到实验,那问题们马上又来了,我们自己的程序链接地址用哪个? 总不能靠掷色子决定吧! 别急!咱不是有红宝书吗? 打开红宝书,翻到14章第三节
这里明确告诉我们了internal rom 的地址是0xffff0000,哪就好办了,把我们的链接地址指向这里,我们的裸机程序被搬搬运到这里后,不就能自己运行了么! 太聪明了! 重复上面的过程, arm-linux-gcc -c start.s -o start.o arm-linux-gcc -c main.c -o main.o arm-linux-ld -Ttext=0xffff0000 start.o main.o -o main arm-linux-objcopy -I elf32-littlearm -Obinary main main.bin 注意啊,链接地址改成这个了。 再把生成的main.bin用winhex放到SD卡的的0x400位置处,对,这里就是2ndboot的老家。 根据前面的partmap.txt文件内容,我开始尝试的是存放到0x200处。一直不成功,后来请教了一个前辈,才开始试验过这个地址。 插卡,上电,果然不出所料,小pi2小蓝眼睛眨得依然那么迷人。 至此,我们的实验都成功了。 总结与疑惑: 1、 我们可以通过替换官方提供的2ndboot、u_boot来达到运行我们自己的裸机程序的目的。 2、 截取友善官方镜像时,发了一个NISH标志,请教友善的客服,没有理睬我,后来靠度娘查到了网友的文章,原来是存放配置信息 ,包括(启动方式,PLL,DDR)。在这里吐槽一下友善的售后,不是说东西便宜,你就可以不用理睬客户的咨询了。人心伤了,在弥合就难了。 3、 由于internalrom没有源代码,没有串口输出,小pi2又没有引出调试接口,无从知道它运行完后,程序会跑到哪里执行,只能靠datasheet反复做实验验证,最后还是靠网友的帮助才成功。这里我要吐槽一下三星,这玩意又不涉及商业机密,你提供一个地址得节省我多少时间啊。 4、 在ubuntu下烧写的sd卡的partmap.txt和windows下所给的镜像扇区位置不一致。位置有重叠,咨询了友善的售后,你知道结果的。我在这里浪费了一周的时间。才把裸机程序运行起来。算了,我懒得吐槽了,谁让我不熟悉这个片子呢! 5、 土财主三星的Datasheet的资料不够详细,尤其关于引导部分的,说到这里我要表扬一下友善的攻城狮,就凭借这本小册子就把android跑起来了。这水平那就不是用牛字能评价的了。 6、 悬而未局的问题,网友怎么知道NISH.txt的内容的,反编译吗? 总之,折腾的过程就是折腾,折腾,再折腾! 我享受这个过程! 不韶了,愿意听我韶的同学,下篇再见。
`
|