刚从硬件转到嵌入式软件的时候,没有任何准备。一入职,领导就交代了一个非常艰巨的任务——在stm32上移植linux!

一瞬间,我傻了。我不能咬紧牙关。首先,我收集了数据。以前在ok6410的板上运行linux,现在移植到stm32上。以前玩stm32,研究生期间也在做。但是现在我从烙铁的硬件中恢复过来之前,已经转向了嵌入式软件的开发。更麻烦的是stm32没有MMU!没有MMU!找一下,好的,有个uClinux!

然后开始学习各种相关知识,了解到linux的启动一般是u-boot——“liunx内核——”根文件系统,所以首先要做一个基于stm32的U-boot,初始化时钟,外设,中断等等。看了韦东山老师的视频感觉很好,从无到有了解了很多。

将u-boot移植到stm32f407

其实说白了,u-boot就是一个裸板程序,和走马灯,串口通讯一样的性质。裸板程序从守时atom的stm32开发板学到了很多,在我的研究生阶段积累了一点。首先,我指的是写这篇博文的大神http://www.cnblogs.com/fozu/p/3618076.html,写的程序。这篇文章写得不错,后面也分析了内核。反复阅读,受益匪浅。这个程序不是u-boot程序,但是和初始化的效果一样。。最后,传递内核参数,跳转到内核。。。当初用keil编译这个程序,结果是一堆错误。别人用的板和你用的板不一样。硬件和串口的led灯可能连接方式不同。比如人家用串口1,你用串口2,缺了一些头文件,就会出错。所以根据我们自己的实际情况,花了一段时间才改正过来,最后去掉所有错误,编译成功。

这时候用的板子是stm32f103。早在2008年ST就发布了u-boot和支持它的uClinux内核,但是只有Uclinux内核有源代码,u-boot给了一个十六进制文件的尴尬。其实cortx m3和cortx m4之前的架构差别很大,这样修改对我来说无疑是非常困难的。我太大了,听不进去,一个人去,做不到!然后先获取stm32f103,复制之前编译无误的引导程序,在stm32的0x08003000位置复制官方提供的uClinux内核。一旦启动,连接串口,打开串口助手,看到什么都没有。。。

怎么了?仔细想想,首先要看最后一步跳转到内核是否成功,然后先验证这一步,参考atoms的IAR跳转历史,编译一个ticker跳转程序,即引导程序不变,地址0x0800000复制,而ticker程序在地址0x08003000复制。如果led灯亮灭,说明跳转是正确的,所以开始的时候灯不亮。疯狂是怎么回事?经过仔细调查,发现是一个跳转函数。引导程序是参考u-boot源代码编写的。里面的函数被分配了一个带函数指针的地址,最后跳过了。经过两天对原子程序修改的辗转反侧,灯居然可以开关了。我不知道现在是什么问题,但至少现在我可以实现跳跃了。

然后把内核复制到0x08003000,一旦启动,串口助手还是没有输出,真的很烦很压抑。stm32f103无法修复,想做stm32f407。。。之后开始找原因,做修改,领导各种提醒。我是在stm32f103和stm32f407这两块板之间做的,然后休息的时候看了魏老师的视频,找资料看看有什么启发,还是没有进展。

后来在网上找了个哥们,在stm32f407上成功移植了u-boot,有启动图,就想,为什么不行?然后我继续搜索,终于在网上找到了u-boot的源代码。我根据自己的stm32f407板修改了串口和时钟,安装了相应的交叉编译链。请注意,arm-non-eabi没有linux,因为它是一个与linux无关的裸板程序。然后我就跑了,最后在串口助手里看到了久违的u-boot启动图。我欣喜若狂!想想那段时间,我真的是在压力下长大的,感觉自己的技术进步很大。

当领导过来的时候,他看到了一只u型靴。。。)说要加外部sram驱动,为了运行linux内核,SRAM只有512K,这么小能运行linux内核?这是另一个故事。首先将sram驱动程序添加到u-boot。

首先参考原子sram程序修改运行。结果可以运行,但可以重新读写。几个地址的数据总有错误。所以我一直在努力思考,想到了一种可能。stm32的FSMC配置用于驱动外部静态随机存取存储器。它有btcr寄存器设置,分为bcr和btr设置。原子开发板用1M16位,我的用512k8位。在btr寄存器设置中,它应设置为8位

下一步是给u-boot添加sram驱动。这个u-boot写的不错,但是配置了外置的8M sdram,所以我会在sdram_init函数中加入sram配置代码,删除掉所有原来的sdram配置代码。折腾了两天,写修改成功。机器一开机,串口助手就正常输出启动信息,用u-boot的md和mw指令验证sram驱动是否可行。也有一些问题,如在前100个地址中写入ff,当md查看时有几个地址数据是错误的,没有显示ff,使用以前的sram裸板程序时也是如此。当我以为软件程序肯定没问题的时候,是硬件问题。还好硬件做了一段时间,不然被公司的硬件工程师坑了。用万用表仔细测试后,发现sram的几条数据线被虚拟焊接。怪不得数据错了。拿个烙铁拖着,好!数据正常,嗯!想成为一名合格的嵌入式软件工程师,还是要软硬件结合,不能脱离硬件!!!!

好的。到目前为止,基于stm32f407的u-boot已经移植成功,加上一个外接的2M SRAM驱动,最后拍了u-boot启动图。我人生中在CSDN的第一篇博文,我希望我将来能继续学习和提高我的技能并努力工作!

将uClinux内核移植到stm32f407

以上先描述了基于stm32f407的u-boot移植,下面将提到移植stm32f407内核最困难的部分。我也在网上找到了这个内核的源代码,介绍是外国大神修改的。真的很感谢这位大神,因为网上资源很多,要善于挖掘和搜索。

我不小心写下了内核代码。刚拿到代码的时候对在stm32f407上运行uClinux没有太大信心。第一,网上没有关于在stm32f407上运行uClinux的信息。都是对网上在stm32上运行uClinux态度不好。的确,stm32运行的是uClinux系统,资源稀缺。而stm32f407的内部flash只有1M 空,其中u-boot占用128K,所以内核存储在0x08020000,剩下的900k 空用,我的板外有2M SRAM,更糟糕的是得到的代码是基于stm32f429的uClinux。很多人在stm32f429上成功运行过,但从来没有在stm32f407上运行过,但我没有退路。项目需求和领导要求只能盲目改变。其实把stm32f103换成stm32f429好很多。最起码stm32f429的架构和stm32f407大致相同,于是我按照自己的板子改了。遇到很多问题,想过放弃,但至少坚持下来了。因为赶时间看了很多书,查了很多资料,了解了很多关于u-boot和内核代码的东西。

我特别感谢jserv先生。当我无处可去时,我给他发了几封电子邮件。他回答了我两个极其重要的问题,建议外置512K至少要换成2M SRAM,不然内核真的会运行。中间,仁慌…...

然后修改stm32f407的内核代码。stm32f429用串口3,我用串口1。时钟错了,换吧!存储地址不同。换吧!Stm32f429不仅有外部SRAM,还在空之间的8M处有NOR闪存。它财大气粗,随便用资源,不像我的stm32f407,只有外置的2M SRAM,还好uClinux代码是在XIP模式下运行的,也就是把代码段放在内部flash中本地执行,把bss段的数据段和其他段放在sram中运行,所以,空就够了。

在此期间,有这样一个问题:

我被卡住了一个星期,当时很疑惑。创建缓存时出现内核错误,无法再运行。我仔细对比了一下stm32f103的uClinux源代码,没有发现错误。一个多星期没进步,内核慌了我就慌了。还好领导了解情况后没有催我,而是给我买了一本《ARM Linux内核源代码解析》,让我好好研究一下。为了解决这个问题,我看了关于构建kmem缓存的文章。linux内核源代码太复杂,让我头大。后来我觉得这不是解决办法,又是硬件问题吗?因为原来512k的sram升级到了2M,公司的硬件工程师又修改了一遍,于是我又用电烙铁把stm32芯片、sram芯片和它们之间的上拉电阻焊接了一遍,一上电就正常运行到下一步。唉,之前移植了u-boot的sram驱动也是我的硬件坑。不知道一些硬件的时候真不敢相信。。。

然后,前后用了将近两个月,是这样的:

-我很幸运能想到它。。。

遇到的下一个问题是缺少根文件系统。这段uClinux代码本来配备了一个根文件系统,就是romfs,但是存储空不够。

UClinux的根文件系统无法挂载,因为最初配置的根文件系统是romfs,基于stm32f429。stm32f429的内部闪存在空之间是2M,romfs在空之间占用300kb以上,这显然是足够的,但是对于stm32f407来说,其内部闪存空是1M,所以这样存储的话,存储空空间不够所以根据这种情况,我认为有必要构建一个占用内存空的较小的initramfs作为uClinux的根文件系统来挂载。

建立stm32f407-uClinux的initramfs根文件系统

如上所述,当内核运行以释放init内存:8k时,就会卡住,无法再运行。查阅相关资料,推测是因为缺少根文件系统造成的。原内核源代码是一个带根文件系统的bin文件,是romfs但是没有源代码。如前所述,我目前的项目使用的是stm32f407,内部闪存容量和外部SRAM不足以复制这个暂停为根文件系统使用的原始romfs。

下一步是找到一个经济适用的文件系统作为内核的根文件系统。在网上查了一下相关资料,可以知道YAFFS2支持nandflash,jffs支持nor flash,好像不适合我手里的stm32f407。于是我仔细研究了stm32f103的源代码,发现它有两种启动模式。一种是使用iniramfs作为根文件系统,启动xip。在stm32f103中,当flash只有512k的时候,实际运行的是Uclinux。另一个是jfss2挂在外部nor flash上。显然我只能参考第一种方式,用INIRAMFS作为根文件系统。然后开始构建initramfs相关文件。在menuconfig中,仔细研究了stm32f103 XIP启动模式的内核配置。config _ initramfs _ source = " initramfs-filelist "而initramfs-file list位于uclinux/Linux-2.6.x下,乍一看是这样的:

一开始,我无法理解shell的含义。在网上找到一篇文章,写的很清楚。复制并学习它:

将initramfs编译到内核中

使用initramfs最简单的方法是用已经制作好的cpio.gz替换内核中的空一个。这是2.6内核自然支持的,不用做什么特殊设置。

有一个config _ initramfs _ source.该选项指向内核打包initramfs所需的所有文件。默认情况下,这个选项会留下空,所以内核编译后,initramfs也是空,也就是前面提到的rootfs什么都不做。

Config _ initrams _ source可以是绝对路径,也可以是从内核的顶部构建目录开始的相对路径。有三个目标:一个已经制作好的cpio.gz,一个为制作cpio.gz准备好所有内容的文件夹,或者一个文本配置文件。第三种方法最灵活。下面依次介绍这三种方法。

1)使用已经完成的cpio.gz文件

如果您已经有了自己的initramfs_data.cpio.gz文件,您可以将CONFIG_INITRAMFS_SOURCE指向它,内核构建将自动检测文件类型并将其链接到生成的内核映像中。

您也可以将CONFIG_INITRAMFS_SOURCE保留为空,而是将您的cpio.gz文件复制到您的内核的构建目录中。如果不需要,内核的makefile不会生成新的存档。

无论哪种方式,如果你构建一个这样的内核,你可以在不提供外部initrd映像的情况下启动它,并且它仍然可以通过在rootfs之外运行你的init程序来完成它的启动。这是第二种包装方法,如果你现在想试试的话。

2)给内核分配一个文件或文件夹

如果CONFIG_INITRAMFS_SOURCE指向一个目录,内核将为您将其存档。这是创建initramfs归档文件的一种非常简单的方法,也是方法3。

有趣的是,内核构建没有使用标准的cpio命令来创建initramfs档案。你甚至不需要在你的内置系统上安装任何cpio工具。相反,内核构建生成一个用“gen_initramfs_list.sh”描述目录的文本文件,然后将该文件馈送给一个名为“gen_init_cpio”的程序,该程序创建了cpio档案。这看起来像下面这样:

s/gen _ INITRAMFS _ list . sh $ CONFIG _ INITRAMFS _ SOURce & gt;usr/initramfs_list

usr/gen _ init _ cpio usr/initramfs _ list & gt;usr/initramfs_data.cpio

g usr/initramfs_data.cpio

为了打包我们的hello world程序,您可以简单地将其复制到自己的目录中,将其命名为“init”,在该目录中指向CONFIG_INITRAMFS_SOURCE,然后重建内核。最终的内核应该通过打印“hello world”来结束引导。如果您需要调整该目录的内容,如果有任何变化,重建内核将重新打包该目录的内容。

这种方法的缺点是,如果您的initramfs有设备节点,或者关心文件所有权和权限,您需要能够在目录中创建这些东西,以便复制。如果您没有根访问权限,或者正在使用像cygwin这样的交叉编译环境,这是很难做到的。这就是第四种也是最后一种方法。

3)使用配置文件initramfs_list告诉内核initramfs在哪里

这是最灵活的方法。内核的gen_initramfs_list.sh创建一个文本描述文件,列出initramfs的内容,gen_init_cpio使用这个文件生成一个存档。该文件是一个标准文本文件,易于编辑,每个文件包含一行。每一行都以一个关键字开始,表示它描述的条目类型。

创建“hello world”initram fs的配置文件只需要一行代码:

file /init usr/hello 500 0

这将文件“hello”打包,使它在rootfs中显示为/init,权限为500,uid和gid为0。它希望在内核构建目录下的“usr”子目录中找到源文件“hello”。

若要亲自尝试,请将“hello”复制到内核构建目录中的usr中,将上面的配置行复制到它自己的文件中,使用“make menuconfig”将CONFIG_INITRAMFS_SOURCE指向该文件,运行内核构建,并测试引导新内核。或者,您可以将“hello”文件放在其自己的目录中,并使用“s/ gen_initramfs_list.sh dirname”创建一个配置文件。对于大型项目,您可能希望使用生成初始配置,然后使用任何文本编辑器对其进行自定义。

该配置文件还可以指定设备节点、目录、符号链接、命名的FIFO管道和unix域套接字。通过在内核构建后运行“usr/gen_init_cpio”,可以获得关于该文件格式的完整文档。

包含设备节点和符号链接的更复杂的示例如下所示:

dir /dev 755 0 0

nod/dev/console 644 0 c 5 1

nod /dev/loop0 644 0 0 b 7 0

dir /bin 755 1000 1000

slink /bin/sh busybox 777 0 0

file/bin/busybox initram fs/busybox 755 0 0

目录/进程755 0 0

dir /sys 755 0 0

dir /mnt 755 0 0

file/init init ramfs/init . sh 755 0 0

配置文件方法的一个显著优点是,任何普通用户都可以创建一个,指定所有权和权限,并在initramfs中创建设备节点,而无需对构建系统拥有任何特殊权限。使用cpio命令行工具创建一个cpio归档,或者将内核构建指向一个目录,需要一个包含initramfs将包含的所有内容的目录。配置文件方法只需要几个源文件和一个设计文件来获取数据。

这在从其他环境进行交叉编译时也很方便,在这些环境中,本地文件系统甚至可能无法复制initramfs应该包含的所有内容。

总而言之

这四种向rootfs提供内容的方式有一个共同点:内核启动时,一系列文件被解压到rootfs,如果内核能在其中找到可执行文件“/init”,内核就会运行它;这意味着内核将不再关心“root=”指向哪里。

另外,一旦initramfs中的init进程运行,内核就会认为启动已经完成。接下来,init将控制整个宇宙!它有无敌的Process ID #1预留给它,它会创建整个系统接下来的所有部分!此外,它的地位是不可剥夺的。嗯哼,如果PID 1退出,系统会恐慌。

接下来,我将介绍一些init程序在rootfs中可以做的其他事情。

脚注1:内核不允许卸载rootfs的原因和它不会让第一个进程被杀死的原因一样。装载和进程列表从不为空的事实简化了内核的实现。

脚注cpio格式是将文件组合在一起的另一种方式,比如tar和。这是一种更古老、更简单的存储格式,可以追溯到最初的unix,它是在转速包中使用的存储格式。它不像tar或那样被广泛使用,因为cpio命令的命令行语法并不必要的复杂。幸运的是,我们不需要使用这个命令。

脚注3:如果PID 1退出,内核总是会死机;这与initramfs无关。所有可能杀死init的信号都被阻止,甚至“kill -9”也会可靠地杀死任何其他进程。但是init仍然可以调用exit syscall本身,如果在PID 1中发生这种情况,内核就会死机。在这里避免它主要是一个表面问题:我们不想恐慌滚动我们的“你好世界!”屏幕顶部的消息。

脚注4:针对glibc的静态链接程序会产生庞大、臃肿的二进制文件。是的,对于hello world proram来说,这一数字预计将超过40万。您可以尝试在生成的二进制文件上使用“strip”命令,但这没有多大帮助。这种膨胀是uClibc存在的原因。

脚注5:旧的2.6内核有一个错误,他们会附加到重复的文件,而不是覆盖。在依赖此行为之前,请测试您的内核版本。

脚注6:用户模式Linux或QEMU对测试initramfs非常有帮助,但这超出了本文的范围。

脚注7:嗯,算是吧。默认的版本可能是空的,但是由于一个小错误,2.6.16内核中的版本实际上包含一个“/dev/console”节点和一个“/root”目录,这两个目录不用于任何用途。它压缩到大约135字节,实际上可能是空的。在英特尔上,您可以运行“readelf -S vmlinux”并查找“init.ramfs”部分,查看链接到2.6内核的cpio.gz档案。Elf部分的名称在其他平台上可能会有所不同。

显然,stm32f103使用的是第三种方法。里面的说明无非是设置文件权限,设置软连接等。期间也学了一点bash shell,收获了一点。明白了道理之后,就好办了。我直接跟着猫画老虎。我用了第二种方法:

1.首先创建rootfs文件夹,然后在这个文件夹下创建bin,dev等,proc,sys等目录。

2.编译busybox并将生成的bin文件复制到rootfs/bin

3.创建一个新的linuxrc文件,设置权限chmod 777,然后将init=/linuxrc添加到从u-boot传输到内核的参数中

4.在dev目录中添加一个设备节点,否则不会有输出信息!

1 mknod -m 666控制台c 5 1

2 mknod -m 666 null c 1 3

5.在内核make menuconfig上,CONFIG_INITRAMFS_SOURCE= "您刚刚构建的文件夹的绝对路径"

编译内核,initramfs是直接用内核编译的,不需要单独一份bin文件,更方便启动,相关显示信息可以在串口调试助手里看到:

最后说一下自己的感受。虽然使用initramfs根文件系统方便实用,但缺点是只读不可写,非常不利于开发。领导说要加一个spi闪存,在里面挂载一个根文件系统,那是未来的事,以后再说。目前这种情况只能达到我的技术水平。添加根文件后,stm32内部闪存中有超过200k的存储空间空,所以我应该可以添加一些驱动程序和应用程序。然后我的任务就是写简单的驱动和应用。嗯,我还要继续学习,再努力。。。

1.《linux移植 如何在STM32上移植Linux?超详细的实操经验分享》援引自互联网,旨在传递更多网络信息知识,仅代表作者本人观点,与本网站无关,侵删请联系页脚下方联系方式。

2.《linux移植 如何在STM32上移植Linux?超详细的实操经验分享》仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证。

3.文章转载时请保留本站内容来源地址,https://www.lu-xu.com/tiyu/1757397.html