当然本业还是搞单片机,之前也跑过st官方给103评估板的uclinux BSP包,可惜uclinux太大了,只能跑外扩flash上,fsmc的带宽太低,一个ls就要等几秒。不过当时觉得能跑Linux是单片机的最大荣誉,也相当乐在其中。
两年前偶然看到某博客,说Linux内核支持stm32云云,Emcraft也猛然放出支持4.2内核的它自己的STM32 SOM的BSP。随后翻了一下当时的主流内核发现,stm32_defconfig赫然在列,设备树啥的都有模有样。想必里面大有文章可做。
但是社区的玩法就不像基于BSP的二次开发那么工具齐全了,东拼西凑起来的东西要不断地试探,有时候人品不好,bug死活调不出来。大二时候好不容易将uboot在103的板子上跑通,加了nand flash之类的驱动,然后将uclinux 2.6的内核往死里裁剪,网络、模块、sysfs、proc啥都不要了,裁到400k,并且将data段丢到外部flash中,内核总算塞进了内部flash。busybox也基本裁剪到只剩下hush,用romfs跑起来了。
在片内flash里面xip,性能远胜于fsmc挂的nor、sram,shell基本感觉不到延迟。。
后来就是考试、面试、比赛、实习,终于决定不浪费读研资格,前些日子就到实验室搬砖去了。不过我一直仍想弄出个像样子的Linux电脑来,毕竟离它只有一步之遥了,也算是大学期间最有成就感的作品了。
等我真正开始弄的时候,最新的Linux内核是4.13,在对stm32的支持中,stm32f429是最为完整的,基本上啥都有了,USB、网卡、dcmi、ltdc都有了。值得一提的是,ltdc和dma2d的驱动在源码driver/gpu/drm/stm目录下面是单独的目录,st真是志不在小。。。
stm32429-eval板子的配置是一个很好的例子,网卡、显示屏、摄像头、USB都写了。stm32f429-disc1的支持也不错。利用最新的arm-none-eabi工具链,默认配置下,编译出来内核大小1.2MB左右,可以塞进I系列的片子里。
按照defconfig的配置,内核起始地址0x08008000,前面还有32k的大小放bootloader。afboot是在github上开源的专门为stm32各种板子写的Linux的bootloader,编译出来的bin文件只有2k左右。加上十几kb的设备树,恰好放得下。
利用手上的discovery板,用elinux现成的rootfs,作为initramfs编译进内核,进入hush可以执行一些指令,不过并没有framebuffer,点点灯还是可以的。。。
emcraft只为discovery提供了uclinux的BSP,至于它的4.x系列的BSP,都只能用它那一套编译系统,内核抽出来编译是不行的。他们用的arm-uclinuxeabi工具链,gcc 4.4,也弄不了新版本的内核。
我开始构思自己的板子,stm32f429iit6芯片,带上尽可能多的外设。用cubemx来帮忙安排引脚,主芯片,加上64MB的SDRAM、128MB的nand flash、rmii接口的网卡、两个全速USB、ltdc显示器接口、摄像头、sdio,剩下的就当GPIO,接几个led灯吧。
内存和flash都放主芯片背面,没地方走等长线,不过线长也只有两三厘米,时钟也只有84MHz,不等长不做阻抗也罢orz
既然是“电脑”,就应该有个显示器接口,之前玩FPGA时候有用电阻网络搭成简易dac来做vga的例子,而ltdc就是并口rgb加上同步、使能信号。dac加上同步信号,这样就可以伪装成正经的vga了。
画板子用的kicad。记住快捷键,原理图封装布线都挺顺手。布线时候在OpenGL模式下面横行,比ad流畅多了。
板子到手,焊接、测试。。值得一提的是电源引脚附近的104电容、vcap的2.2u电容一个也不能少,刚焊好主芯片、电源,就可以用jlink识别了,但是烧不了程序,电容焊齐了,就可以了。
然后就开始了漫长的裁剪和驱动搬砖了。。。
刚开始时,先移植一个能跑的uboot去测试内存、flash和网络。主频设为168MHz。之所以不将主频拉到180MHz,就是因为USB需要48M的时钟,不能整除180。在84MHz的时钟频率下,cas设为2,内存顺序读写速度大约能到70MB每秒。nand flash读写就有点慢了。至于网卡,焊好了,而且主机不用WiFi的话,tftp是又快又准的。
刚加上网络,内核就猛增到2MB。。。为了测通内核的驱动,内核只能先放在SDRAM上跑吧,到时候弄内核模块再说。奇怪的是,uboot的tftp好好的,内核的网卡初始化就死了。单步调、看寄存器,折腾了许久,发现源码里一个寄存器写错了,以至于mii接口的没问题,rmii的就不行,怪不得eval板子上能用网卡。。
网卡初始化时候没有使能phy芯片。。。
默认配置是tickless内核,这会导致网卡初始化时候卡死。。。
yaffs的补丁打进去之后编译不过,后来发现是新版内核为解决y2038千年虫问题而去掉了一些接口。。。
USB寄存器配置顺序不对,导致初始化失败。。。
因为在SDRAM上跑的内核性能太低了,USB外接的设备都跑的比stm32快,以至于u盘键盘什么的几乎总是枚举失败。。。
ltdc驱动忘了注册时钟,导致内核最后将ltdc关掉,以至于没有显示。。。
sdio驱动更新了,数据结构变了,然后用不了了。。。
摄像头初始化时候没有使能。。。
。。。
驱动搬好了,内核就爆到快5MB了,然后要想办法裁剪到2MB以下,而且内核的网络是不能模块化的。。。
内核加载模块失败,后来发现是gcc的参数没用长跳转,无法从0x08000000的片内flash跳到0x80000000的SDRAM那里。。。
还有日常hardfault:内存没调好,hardfault;设备树写错了,hardfault;还没进start_kernel(),hardfault。。。
我记得的最吊诡的hardfault是,自己编译的gcc6.4的arm-uclinuxeabi工具链,短跳转的地址算错了,翻了binutils的源码发现新版的as为了支持armv8-m的cpu改了一些东西,导致往前跳的地址多了1,导致PC值为偶数就进入arm模式,然后报用法错误,*hardfault。。。
也碰到过几次imprecise的bus fault,有的是因为写访问了片内flash,有的是外设寄存器没设好,有的发生在初始化中断向量表时候。。。
这一堆乱七八糟的问题解决了,终于,Linux跑起来了。。。
之前在SDRAM里跑,BogoMIPS是9;在片内flash里跑,BogoMIPS暴涨到110。。。
更多视频:www.makeru.com.cn/?t=12
不过回味起来,这只能说业余时间弄的玩具。可以说如今搞自动化的都搞slam,搞计算机的都搞ai,搞系统开发这玩意的早就不那么吃香了罢。